| # Onboarding NPIs to tlBMC |
| |
| go/tlbmc-onboarding-npis |
| |
| <!--* |
| # Document freshness: For more information, see go/fresh-source. |
| freshness: { owner: 'tlbmc-dev' reviewed: '2025-03-12' } |
| *--> |
| |
| This document outlines the additions necessary to Entity-Manager (EM) |
| configuration files to onboard an NPI to tlBMC. |
| [Entity-Manager](https://github.com/openbmc/entity-manager) provides mappings |
| from hardware components to software components within a system, these mappings |
| are managed through EM configuration files as described here. |
| |
| Note: tlBMC requires only additions to existing EM config files, no deletions or |
| modifications of existing properties should occur. This allows tlBMC to be |
| backward compatible with existing bmcweb without conflicting behavior. Original |
| design doc can be viewed at go/tlbmc-data-completeness |
| |
| Tip: Existing daemons, e.g. PSU sensors, will still coexist if not disabled |
| explicitly. Whether these should be configured depends on the use case. If the |
| sensor is only used in telemetry reporting not control, e.g. PID, tlBMC can |
| fully replace existing daemons. |
| |
| [TOC] |
| |
| ## Chassis |
| |
| ### ProbeV2 |
| |
| The addition of the ProbeV2 field makes the config detectable by tlBMC. The |
| value of the `IpmiFru` JSON within the ProbeV2 field corresponds with the |
| existing `Probe` field in EM configs. `BOARD_CONFIG_NAME`, |
| `PRODUCT_CONFIG_NAME`, and `BOARD_PART_NUMBER` are valid probe fields, and if a |
| scanned FRU matches these fields, the object will be created from this config. |
| |
| An example definition: |
| |
| ``` |
| ... |
| "ProbeV2": { |
| "IpmiFru": { |
| "BOARD_PRODUCT_NAME": "foo" |
| } |
| }, |
| ... |
| ``` |
| |
| The `ProbeV2` field can also be set to `TRUE` to always be populated regardless |
| of matching a scanned FRU, or it can be set to `FALSE` to be ignored. |
| |
| An example definition: |
| |
| ``` |
| ... |
| "ProbeV2": "TRUE", |
| ... |
| ``` |
| |
| #### Logical AND and OR Syntax |
| |
| Logical AND and OR syntax is supported for the IpmiFru field if given an array |
| of JSON objects instead of a single JSON. |
| |
| The logical AND operation is applied for all fields within a single IpmiFru |
| JSON. For the matcher to evaluate as True, every field must be a match. |
| |
| An example definition: |
| |
| ``` |
| // In this example, the scanned FRU must have both the BOARD_PRODUCT_NAME = foo |
| // and the BOARD_PART_NUMBER = 1234567890 to successfully probe to True |
| ... |
| "ProbeV2": { |
| "IpmiFru": [ |
| { |
| "BOARD_PRODUCT_NAME": "foo", |
| "BOARD_PART_NUMBER": "1234567890" |
| } |
| ] |
| } |
| ... |
| ``` |
| |
| The logical OR operation is applied between matcher objects within the array of |
| IpmiFru JSONs. If any one of the matchers evaluates to True, considering the |
| logical AND syntax above, the Probe will be evaluated as True. |
| |
| An example definition: |
| |
| ``` |
| // In this example, the scanned FRU must have either the BOARD_PRODUCT_NAME = foo |
| // or the BOARD_PART_NUMBER = 1234567890 to successfully probe to True. If either |
| // field matches, the probe will be successful regardless of the value of the other field |
| ... |
| "ProbeV2": { |
| "IpmiFru": [ |
| { |
| "BOARD_PRODUCT_NAME": "foo" |
| }, |
| { |
| "BOARD_PART_NUMBER": "1234567890" |
| } |
| ] |
| } |
| ... |
| ``` |
| |
| Note: `FOUND` behaviors are not yet supported by tlBMC. |
| |
| #### SubFrus |
| |
| Some objects are represented as subfrus of a single detected FRU object. In this |
| case, subfrus are a logically separate Chassis but belong to the same FRU |
| object. Subfrus are defined by a separate EM config from the base FRU object, |
| but must have `IsSubFru: true` in their ProbeV2 configuration. |
| |
| A config with `IsSubFru: true` will create a separate redfish resource with its |
| own Ports/LocationContext/devpath/Asset information. Multiple subfrus can be |
| linked to the same base Fru object. |
| |
| All subfrus should have the PartLocationType PART_LOCATION_TYPE_EMBEDDED. This |
| indicates that the subfru object is not a physically separate entity. |
| An embedded object will have its ServiceLabel overridden as its config name to |
| ensure a unique identifier. |
| |
| An example definition: |
| |
| ``` |
| ... |
| "ProbeV2": { |
| "IpmiFru": [ |
| { |
| "BOARD_PRODUCT_NAME": "foo" |
| } |
| ] |
| "IsSubFru": true |
| } |
| "PartLocationType": "PART_LOCATION_TYPE_EMBEDDED" |
| ``` |
| |
| ### ResourceType |
| |
| `ResourceType` has parity with the `Type` field in existing EM configs. |
| |
| Supported resource types are defined in `tlbmc/resource/resource.proto`. |
| |
| An example definition: |
| |
| ``` |
| ... |
| "ResourceType": "RESOURCE_TYPE_BOARD", |
| ... |
| ``` |
| |
| #### ChassisType |
| |
| For configs of `RESOURCE_TYPE_BOARD` an `ChassisType` is needed as a additional |
| field. This field has parity with the EM config field |
| `xyz.openbmc_project.Inventory.Item.Chassis.ChassisType`. This field will |
| default to type `RackMount`. |
| |
| Supported chassis types are defined in `tlbmc/resource/resource.proto`. |
| |
| An example definition: |
| |
| ``` |
| ... |
| "ChassisType": "CHASSIS_TYPE_COMPONENT", |
| ... |
| ``` |
| |
| ### Downstream/Upstream Ports |
| |
| Physical ports are modeled as downstream/upstream ports between Chassis. These |
| are fields in the `Exposes` array of a config and may correspond to existing |
| ports. Similar to existing EM configs, a port on an upstream chassis to a |
| downstream chassis has a `Label` field, used to populate the devpath. These |
| ports will have the `Type` of `PortDownstream`. On the downstream chassis, there |
| will be a corresponding port with the same name, but the `Type` will be |
| `UpstreamConnection`. |
| |
| Tip: Only one chassis (the root chassis) will have no `UpstreamConnection` or |
| `CableUpstreamConnection`. This is enforced by tlBMC and the store will fail to |
| create if this condition is not true. |
| |
| An example of a `PortDownstream` port: |
| |
| ``` |
| "Exposes": [ |
| ... |
| { |
| "Label": "bar", |
| "Name": "Foo $bus Port", |
| "Type": "PortDownstream", |
| "User": "tlbmc" |
| }, |
| ... |
| ], |
| ``` |
| |
| and the corresponding `UpstreamConnection` port: |
| |
| ``` |
| "Exposes": [ |
| ... |
| { |
| "Name": "Foo $bus Port", |
| "Type": "UpstreamConnection", |
| "User": "tlbmc" |
| }, |
| ... |
| ], |
| ``` |
| |
| Note: The name field must match exactly between `PortDownstream` and |
| `UpstreamConnection` ports. This is how topology is created and `contains` and |
| `contained_by` associations are populated. |
| |
| ### Substitution |
| |
| Any fields in the `Exposes` field, including all Port configurations will |
| support `$bus` and `$index` substitution. This includes substitution with |
| mathematical expressions. Mathematical expressions are evaluated strictly left |
| to right in substitution and will stop at the end of the string or first |
| non-numeric/operator character encountered. An ill-formed mathematical |
| expression will cause parsing error. |
| |
| Examples of substitution: |
| |
| ``` |
| Assuming bus = 3, |
| |
| "Exposes": [ |
| { |
| "Label": "IO0", |
| "Name": "Port $bus", |
| "Type": "PortDownstream", |
| "REASON": "Valid, simple bus substitution. Evaluates to `Port 3`." |
| }, |
| { |
| "Label": "IO0", |
| "Name": "Port $bus - 5 * 3 + 54", |
| "Type": "PortDownstream", |
| "REASON": "Valid, bus substitution with mathematical expression. Evaluates to `Port 48`." |
| }, |
| { |
| "Label": "IO0", |
| "Name": "Port $bus - 5 * 3 + 54 something", |
| "Type": "PortDownstream", |
| "REASON": "Valid, simple bus substitution with mathematical expression and remainder. Evaluates to `Port 48 something`." |
| }, |
| { |
| "Label": "IO0", |
| "Name": "Port $bus / 0", |
| "Type": "PortDownstream", |
| "REASON": "Invalid, division by zero" |
| }, |
| { |
| "Label": "IO0", |
| "Name": "Port $bus - something", |
| "Type": "PortDownstream", |
| "REASON": "Invalid, hanging operator" |
| } |
| ] |
| |
| ``` |
| |
| ### PartLocationType |
| |
| `PartLocationType` has parity with the `xyz.openbmc_project.Inventory.Connector` |
| field in existing EM configs. |
| |
| PartLocationTypes are defined in |
| [topology_config.proto](https://gbmc.googlesource.com/gbmcweb/+/refs/heads/master/tlbmc/topology_config.proto). |
| |
| Note: All subfrus MUST have a PartLocationType of PART_LOCATION_TYPE_EMBEDDED. |
| tlBMC will error if a subfru is misconfigured to not use the embedded type. |
| |
| An example definition: |
| |
| ``` |
| ... |
| "PartLocationType": "PART_LOCATION_TYPE_SLOT", |
| ... |
| ``` |
| |
| ### Processors |
| |
| `Processor` is a field unique to tlBMC, since these can no longer be detected |
| through dbus, they must be defined in the static configuration. Processors are |
| defined in the `Exposes` field of a config, with a `Name` and `"Type": |
| "Processor"`. |
| |
| An example definition: |
| |
| ``` |
| "Exposes": [ |
| ... |
| { |
| "Name": "cpu0", |
| "Type": "Processor", |
| "User": "tlbmc" |
| }, |
| ... |
| ], |
| ``` |
| |
| ### Storage |
| |
| `Storage` has parity with the `xyz.openbmc_project.Inventory.Item.Storage` |
| field. This field is a JSON with a single `Id` field. The storage field is used |
| to create the link to the associated Storage object in the redfish query |
| response, the value of `Id` should be the `Id` of the associated Storage object. |
| |
| An example definition: |
| |
| ``` |
| ... |
| "Storage": { |
| "Id": "foo_storage_$index" |
| }, |
| ... |
| ``` |
| |
| ### Asset |
| |
| `Asset` has parity with the `xyz.openbmc_project.Inventory.Decorator.Asset` |
| field in existing EM configs. This field allows users to override the |
| Manufacturer, Model, PartNumber, or SerialNumber in the redfish response. Valid |
| substitutions are defined in the same way as in the OpenBMC definition, for |
| example: `$PRODUCT_PART_NUMBER` or `$BOARD_MANUFACTURER` will substitute the |
| respective field from the scanned FRU object. Fields can also be overridden with |
| any string that is not prefixed with `$`. |
| |
| Tip: if a substitution is attempted (prefixed with `$`) but fails, tlBMC will |
| error. |
| |
| An example definition: |
| |
| ``` |
| ... |
| "Asset": { |
| "Manufacturer": "$BOARD_MANUFACTURER", |
| "Model": "foo", |
| "PartNumber": "$BOARD_PART_NUMBER", |
| "SerialNumber": "$BOARD_SERIAL_NUMBER", |
| "User": "tlbmc" |
| }, |
| ... |
| ``` |
| |
| ### Ad-Hoc Frus |
| |
| Certain FRUs require special handling as they may not be scannable when bmcweb |
| comes up. For this purpose, we have created something called an "Ad-Hoc Fru |
| Scanner". This will allow you to configure exactly how you want to scan a FRU. |
| |
| An example is below: |
| |
| ``` |
| "AdHocFruConfig": { |
| "name": "AdHocFru", |
| "i2c_common_config": { |
| "bus": "4", |
| "address": "83" |
| }, |
| "delay_between_reads": "0.001s", |
| "periodic_scan_config": [ |
| { |
| "scan_interval": "10s", |
| "scan_count": 6 |
| }, |
| { |
| "scan_interval": "60s", |
| "scan_count": 9 |
| }, |
| { |
| "scan_interval": "600s", |
| "scan_count": 5 |
| } |
| ] |
| } |
| ``` |
| |
| The proto definition is found |
| [here](https://gbmc.googlesource.com/gbmcweb/+/refs/heads/master/tlbmc/ad_hoc_fru_config.proto). |
| |
| > NOTE: This must go inside the ProbeV2 Json Object. |
| |
| ## Sensor |
| |
| ### HWMon Sensors |
| |
| There are no specific changes that need to be made to HWMon Sensor configs to be |
| compatible with tlBMC. |
| |
| Not all sensor types are currently supported by tlBMC, see |
| kSupportedHwmonTempSensorTypes in tlbmc/configs/entity_config_json_impl.cc. |
| |
| ### PSU Sensors |
| |
| There are no specific changes that need to be made to PSU Sensor configs to be |
| compatible with tlBMC. |
| |
| Not all sensor types are currently supported by tlBMC, see |
| kSupportedPsuSensorTypes in tlbmc/configs/entity_config_json_impl.cc. |
| |
| ### Fan Controller |
| |
| All I2C fans need a corresponding fan controller defined in the EM config that |
| share the bus/address combination as the fans. Without this field, no I2C fans |
| will be populated. |
| |
| An example definition: |
| |
| ``` |
| "Exposes": [ |
| ... |
| { |
| "Address": "0x2c", |
| "Bus": "34", |
| "Name": "FAN_CONTROLLER", |
| "Type": "MAX31790" |
| }, |
| ... |
| ], |
| ``` |
| |
| ### I2C Fans (Tach/PWM) |
| |
| For all I2C configs, there must be a JSON `Connector` field that contains at |
| least `Pwm` (the index of the fan) and `PwmName` (name of the PWM Fan sensor). |
| Some configs may already have this present. No other changes are required to |
| make I2C fans compatible with tlBMC. |
| |
| An example definition: |
| |
| ``` |
| "Exposes": [ |
| ... |
| { |
| "Address": "0x2c", |
| "BindConnector": "fan1_tach_connector", |
| "Bus": "32", |
| "Connector": { |
| "Pwm": 1, |
| "PwmName": "fan1_pwm" |
| }, |
| "EntityId": "0x1A", |
| "EntityInstance": "0x01", |
| "Index": 1, |
| "Name": "fan1_tach", |
| ... |
| } |
| ... |
| ] |
| ``` |
| |
| ### Related Item |
| |
| `RelatedItem` is a field unique to tlBMC, since RelatedItem can no longer be |
| detected through dbus calls, they must be defined in the static configuration. |
| The `RelatedItem` of any sensor must be defined as a `RelatedItem` JSON object |
| within the sensor config. It contains the fields `Id` (the redfish Id of the |
| related item) and `Type` (the `ResourceType` of the related item). By default, |
| the related item will be the parent chassis object. This field only needs to be |
| overridden in the case that the related item should be a different object e.g. a |
| fan. |
| |
| An example definition: |
| |
| ``` |
| "Exposes": [ |
| ... |
| { |
| ... |
| "Name": "fan1_tach", |
| "RelatedItem": { |
| "Id": "Fan1", |
| "Type": "RESOURCE_TYPE_FAN", |
| "User": "tlbmc" |
| }, |
| ... |
| } |
| ... |
| ] |
| ``` |
| |
| ## Cables |
| |
| ### Cable Downstream/Upstream Connections |
| |
| Cables are defined in separate EM config files from their associated Chassis, |
| and are associated through `CableDownstreamConnection` or |
| `CableUpstreamConnection` type ports in their `Exposes` field. All Cable configs |
| should have their `ProbeV2` field set to `TRUE` (since Cables cannot be detected |
| over I2C) and their `ResourceType` set to `RESOURCE_TYPE_CABLE`. The associated |
| upstream or downstream Chassis must have a port with the opposite connection |
| type and the same `Name` to form a connection. Also defined in the exposed port |
| is `CableId`. This field must match the `Name` field in the Cable config. |
| |
| Note: There is a difference between the `Name` field in the port config (name of |
| the port) and `CableId`, which should match the `Name` of a Cable config JSON |
| |
| An example Cable config: |
| |
| ``` |
| { |
| "Exposes": [ |
| { |
| "Name": "Foo Cable Downstream", |
| "CableId": "FooCable", |
| "Type": "CableUpstreamConnection", |
| "User": "tlbmc" |
| }, |
| { |
| "Name": "Foo Cable Upstream", |
| "CableId": "FooCable", |
| "Type": "CableDownstreamConnection", |
| "User": "tlbmc" |
| } |
| ], |
| "Name": "FooCable", |
| "Probe": "xyz.openbmc_project.Network.EthernetInterface({'InterfaceName': '^foo$', 'LinkUp': true})", |
| "ProbeV2": "TRUE", |
| "ResourceType": "RESOURCE_TYPE_CABLE", |
| "Type": "Cable" |
| } |
| ``` |
| |
| The upstream chassis exposes a CableDownstreamConnection: |
| |
| ``` |
| "Exposes": { |
| ... |
| { |
| "Name": "Foo Cable Downstream", |
| "CableId": "FooCable", |
| "Type": "CableDownstreamConnection", |
| "User": "tlbmc" |
| }, |
| ... |
| }, |
| ``` |
| |
| and the downstream chassis exposes a CableUpstreamConnection: |
| |
| ``` |
| "Exposes": { |
| ... |
| { |
| "Name": "Foo Cable Upstream", |
| "CableId": "FooCable", |
| "Type": "CableUpstreamConnection", |
| "User": "tlbmc" |
| }, |
| ... |
| }, |
| ``` |