Onboarding NPIs to tlBMC

go/tlbmc-onboarding-npis

This document outlines the additions necessary to Entity-Manager (EM) configuration files to onboard an NPI to tlBMC. 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. In this case, see the section SkipDbusRead Property which describes how to disable sensor polling over dbus to significantly improve CPU utilization.

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.

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.

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.

NOTE: This must go inside the ProbeV2 Json Object.

Subfrus

Some objects are not uniquely FRU-detectable like a regular chassis and are logically separate from a given chassis, defined in their own EM configuration. These objects are considered Subfrus.

Assemblies

Assemblies are subfrus that may have their own devpath, Manufacturer, Serial Number, Model, etc. Assemblies represent a component that is logically separate but are not I2C scannable and do not have an eeprom.

An assembly is always defined with "ResourceType": "RESOURCE_TYPE_ASSEMBLY", this has parity with the "Type": "Component" field in existing EM configs.

Assemblies will have PartLocationType of either PART_LOCATION_TYPE_EMBEDDED or PART_LOCATION_TYPE_SLOT. This should follow what is in the existing EM config.

  • PART_LOCATION_TYPE_EMBEDDED: This represents an assembly that is physically attached to the parent board and cannot be replaced on its own. An example of this configuration is provided below:

    {
      "Exposes": [
        {
          "Name": "Foo Sensor Controller 0 Port",
          "Type": "UpstreamConnection",
          "User": "tlbmc"
        },
        {
          "Name": "foo_0_variant_a_sensors",
          "Address": "0x68",
          "Bus": "64",
          "Labels": [
            "vin1",
            "iin1",
            "pin1"
          ],
          "vin1_Name": "foo_0_a_vin",
          "iin1_Name": "foo_0_a_iin",
          "pin1_Name": "foo_0_a_pin",
          "Type": "XDPE1A2G5B",
          "TlbmcOwned": true,
          "SkipDbusRead": true
        }
      ],
      "Name": "Foo Sensor Controller 0 Var A",
      "ProbeV2": {
        "IpmiFru": {
          "PRODUCT_PRODUCT_NAME": "^Foo_Parent_Variant_A$"
        }
      },
      "ResourceType": "RESOURCE_TYPE_ASSEMBLY",
      "PartLocationType": "PART_LOCATION_TYPE_EMBEDDED",
      "Type": "Component",
      "xyz.openbmc_project.Inventory.Connector.Embedded": {},
      "xyz.openbmc_project.Inventory.Decorator.Asset": {
        "Manufacturer": "Foo_Manufacturer",
        "Model": "Foo_Controller_Model"
      }
    },
    
  • PART_LOCATION_TYPE_SLOT: This represents a component that is attached to the parent chassis through a slot and can be individually replaced, although it is not FRU detectable. An example of this configuration is provided below:

    {
      "Exposes": [
        {
          "Name": "Bar Port",
          "Type": "UpstreamConnection",
          "User": "tlbmc"
        },
        {
          "Address": "0x42",
          "Bus": "21",
          "Name": "bar_sensor1",
          "Name1": "bar_sensor2",
          "Name2": "bar_sensor3",
          "Labels": [
            "sensor1",
            "sensor2",
            "sensor3"
          ],
          "Type": "TMP75",
          "TlbmcOwned": true,
          "SkipDbusRead": true
        }
      ],
      "Name": "Bar Assembly",
      "ProbeV2": "TRUE",
      "ResourceType": "RESOURCE_TYPE_ASSEMBLY",
      "PartLocationType": "PART_LOCATION_TYPE_SLOT",
      "Type": "Component",
      "xyz.openbmc_project.Inventory.Connector.Slot": {},
      "Asset": {
        "Model": "Bar_Assembly_Model"
      }
    }
    
    

Assemblies may have their own sensors defined. These will be propagated to the parent chassis object since Assemblies cannot directly own sensors. This allows for sensors that will only be present on certain board variants to be defined in a separate config without having to duplicate the whole parent board configuration.

Note: tlBMC does not currently own the Assembly redfish path. The sensor propagation described above is the only feature supported by tlBMC. Querying assemblies at /redfish/v1/Chassis/{ChassisId}/Assembly will be routed through non-tlBMC Bmcweb.

An example definition:

{
    "Exposes": [
      {
        "Name": "Assembly A Port",
        "Type": "UpstreamConnection",
        "User": "tlbmc"
      },
      {
        "Name": "variant_a_psu_sensor",
        "Address": "0x68",
        "Bus": "64",
        "Labels": [
          "vin1",
          "pin1",
          "vout1",
          "iout1"
        ],
        "vin1_Name": "foo_a_vin",
        "pin1_Name": "foo_a_pin",
        "vout1_Name": "foo_a_vout",
        "iout1_Name": "foo_a_iout",
        "Type": "ADM1266",
        "TlbmcOwned": true,
        "SkipDbusRead": true
      }
    ],
    "Name": "Foo Assembly A",
    "ProbeV2": {
      "IpmiFru": {
        "PRODUCT_PRODUCT_NAME": "^FooVariant$"
      }
    },
    "ResourceType": "RESOURCE_TYPE_ASSEMBLY",
    "PartLocationType": "PART_LOCATION_TYPE_EMBEDDED",
    "Type": "Component",
    "xyz.openbmc_project.Inventory.Connector.Embedded": {},
    ...
  },

The sensors defined here in the variant_a_psu_sensor config would be propagated to the parent board, Foo which would be expected to have a PortDownstream connection with the name Assembly A Port. Note again that tlBMC does not own Assemblies, but due to "Type": "Component", being in the config, Foo Assembly A can be queried under /redfish/v1/Chassis/Foo/Assembly.

Sensor

Note: For any sensor configuration to be parsed by tlBMC, must add the property "TlbmcOwned": true to the sensor EM config. This must be added per sensor. tlBMC will error if a sensor is marked as TlbmcOwned but is not a supported type.

HWMon Temp Sensors

There are no specific changes that need to be made to HWMon Temp 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.

SkipDbusRead Property

For Hwmon Temp Sensors and PSU Sensors, if the dbus interface of the sensor is not being consumed by other processes such as PID loop, we can stop polling over dbus to significantly reduce CPU usage on BMC. By adding the property "SkipDbusRead": true in the sensor configuration, the dbus-sensor daemon will stop polling the sensor. tlBMC backend will still supply the reading as expected when queried through redfish.

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"
    },
  ...
},