entity-manager: probe: Refactor matching

In a nutshell, rely on nlohmann's comparison operator to make the match
instead of rolling our own.  A nice side effect is the expected match
behavior is well documented by nlohmann.

Code size and performance are unaffected (after removing dead code):
  before: 25121752 bytes unstripped, 3202608 bytes stripped
   after: 25146552 bytes unstripped, 3202616 bytes stripped
  before: ~22 seconds for 5000 iterations of the tests
   after: ~22 seconds for 5000 iterations of the tests

This refactor reduces the number of lines of code we need to maintain
and test and increases the number of lines of code used from the
extensively tested nlohmann json library.

The refactor simplifies the logic down to:
  return probe == value;
for non-string probes.  A string specialization is still required so we
can pattern match.

The refactor enables support for matching non-trivial json types such as
arrays and objects.  New types need only be added to BasicVariantType.

There are several changed testcase expectations introduced by the
refactor.  Each is discussed in detail below.

 - stringRegexError
 - boolStringError -> boolNeqString
 - falseEmptyError -> falseNeqEmpty
 - trueEmptyError -> trueNeqEmpty
 - uintStringError -> uintNeqString
 - uintEmptyError -> unitNeqEmpty
 - intStringError -> intNeqString
 - intEmptyError -> intNeqEmpty
 - doubleStringError -> doubleNeqString
 - doubleEmptyError -> doubleNeqEmpty
Configuration file validation of regular expression syntax or the
correct json type for a given dbus interface and property is better done
by our offline validation tools.  Avoid runtime complexity attempting to
distinguish between user errors and probe statements that simply do not
match by treating them the same and always return "no-match" instead of
an invalid argument error for user error situations.  stringRegexError
could be an exception - there is no additional runtime complexity
overhead associated with flagging it as a user error, but doing so would
result in inconsistent behavior, so return no-match for it as well.

 - stringZeroEqFalse -> stringZeroNeqFalse
 - stringOneEqTrue -> stringOneNeqTrue
 - stringEqUnit8 -> stringNeqUint8
 - stringEqUint32 -> stringNeqUint32
 - stringEqInt32 -> stringNeqInt32
 - stringRegexEqInt32 -> stringRegexNeqInt32
 - stringEqDouble -> stringNeqDouble
Prior to this patch dbus properties are converted to strings in the
event of a string probe.  This works around mistakes in configuration
files where the wrong json type is used but it can provide unexpected
results like the probe "1" matching a true and the probe "11" matching a
false, and the string "1*2" matching the number 112.  Implementing the
expected behavior is possible but would add complexity.  For these
reasons this refactor drops the string conversion and requires the user
to use the correct types in their configurations.

 - falseEqUint32Zero -> falseNeqUint32Zero
 - trueEqUint32Zero -> falseNeqUint32Zero
 - trueEqDoubleOne -> trueNeqDoubleOne
 - falseEqDoubleZero -> falseNeqDoubleZero
 - uintEqTrue -> uintNeqTrue
 - uintEqFalse -> uintNeqFalse
 - unitEqDoubleRound -> uintNeqDouble
Prior to this patch dbus properties are coerced to unsigned integers in
the event of a boolean or unsigned integer probes.  Like in the previous
section this can have unexpected results such as integral json types
matching floats with fractional components from dbus.  Again,
implementing the expected behavior is possible but would add
coomplexity, and again these conversions are abandoned and instead
require the user to use the correct types in their configurations.

 - intEqDoubleRound -> intNeqDoubleRoound
 - doubleEqFalse -> doubleNeqFalse
 - doubleEqTrue -> doubleNeqTrue
Like the previous two sections, dbus properties are coerced to integers
in the event of an integer probe, and dbus properties are coerced to
doubles in the event of a double probe, and can result in unexpected
behavior. Require the user to use the correct types in their
configurations.

Change-Id: I6ce7a9c4a80d9612b15cd5e7bb4647bd96852e48
Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>
2 files changed
tree: edd23f8a0c54775c58ec34560b98a60a580fe369
  1. configurations/
  2. docs/
  3. include/
  4. schemas/
  5. scripts/
  6. service_files/
  7. src/
  8. subprojects/
  9. test/
  10. .clang-format
  11. .clang-ignore
  12. .gitignore
  13. blacklist.json
  14. Doxyfile
  15. LICENCE
  16. MAINTAINERS
  17. meson.build
  18. meson_options.txt
  19. README.md
README.md

Entity Manager

Entity manager is a runtime configuration application which parses configuration files (in JSON format) and attempts to detect the devices described by the configuration files. It also can, based on the configuration, attempt to load install devices into sysfs. It takes these configurations and produces a best representation of the files on dbus using the xyz.openbmc_project.Configuration namespace. It also produces a system.json file for persistence.

Configuration Syntax

In most cases a server system is built with multiple hardware modules (circuit boards) such as baseboard, risers, and hot-swap backplanes. While it is perfectly legal to combine the JSON configuration information for all the hardware modules into a single file if desired, it is also possible to divide them into multiple configuration files. For example, there may be a baseboard JSON file (describes all devices on the baseboard) and a chassis JSON file (describes devices attached to the chassis). When one of the hardware modules needs to be upgraded (e.g., a new temperature sensor), only such JSON configuration file needs to be be updated.

Within a configuration file, there is a JSON object which consists of multiple "string : value" pairs. This Entity Manager defines the following strings.

StringExample ValueDescription
"Name""X1000 1U Chassis"Human readable name used for identification and sorting.
"Probe""xyz.openbmc_project.FruDevice({'BOARD_PRODUCT_NAME':'FFPANEL'})"Statement which attempts to read from d-bus. The result determines if a configuration record should be applied. The value for probe can be set to “TRUE” in the case the record should always be applied, or set to more complex lookups, for instance a field in a FRU file that is exposed by the frudevice
"Exposes"[{"Name" : "CPU fan"}, ...]An array of JSON objects which are valid if the probe result is successful. These objects describe the devices BMC can interact.
"Status""disabled"An indicator that allows for some records to be disabled by default.
"Bind*""2U System Fan connector 1"The record isn't complete and needs to be combined with another to be functional. The value is a unique reference to a record elsewhere.
"DisableNode"Fan 1"Sets the status of another Entity to disabled.

Template strings in the form of "$identifier" may be used in configuration files. The following table describes the template strings currently defined.

Template StringDescription
"$bus"During a I2C bus scan and when the "probe" command is successful, this template string is substituted with the bus number to which the device is connected.
"$address"When the "probe" is successful, this template string is substituted with the (7-bit) I2C address of the FRU device.
"$index"A run-tim enumeration. This template string is substituted with a unique index value when the "probe" command is successful. This allows multiple identical devices (e.g., HSBPs) to exist in a system but each with a unique name.

Configuration HowTos

If you're just getting started and your goal is to add sensors dynamically, check out My First Sensors

Configuration Records - Baseboard Example

Required fields are name, probe and exposes.

The configuration JSON files attempt to model the actual hardware modules which make up a complete system. An example baseboard JSON file shown below defines two fan connectors and two temperature sensors of TMP75 type. These objects are considered valid by BMC when the probe command (reads and compares the product name in FRU) is successful and this baseboard is named as "WFP baseboard".

{
    "Exposes": [
        {
            "Name": "1U System Fan connector 1",
            "Pwm": 1,
            "Status": "disabled",
            "Tachs": [
                1,
                2
            ],
            "Type": "IntelFanConnector"
        },
        {
            "Name": "2U System Fan connector 1",
            "Pwm": 1,
            "Status": "disabled",
            "Tachs": [
                1
            ],
            "Type": "IntelFanConnector"
        },
        {
            "Address": "0x49",
            "Bus": 6,
            "Name": "Left Rear Temp",
            "Thresholds": [
                {
                    "Direction": "greater than",
                    "Name": "upper critical",
                    "Severity": 1,
                    "Value": 115
                },
                {
                    "Direction": "greater than",
                    "Name": "upper non critical",
                    "Severity": 0,
                    "Value": 110
                },
                {
                    "Direction": "less than",
                    "Name": "lower non critical",
                    "Severity": 0,
                    "Value": 5
                },
                {
                    "Direction": "less than",
                    "Name": "lower critical",
                    "Severity": 1,
                    "Value": 0
                }
            ],
            "Type": "TMP75"
        },
        {
            "Address": "0x48",
            "Bus": 6,
            "Name": "Voltage Regulator 1 Temp",
            "Thresholds": [
                {
                    "Direction": "greater than",
                    "Name": "upper critical",
                    "Severity": 1,
                    "Value": 115
                },
                {
                    "Direction": "greater than",
                    "Name": "upper non critical",
                    "Severity": 0,
                    "Value": 110
                },
                {
                    "Direction": "less than",
                    "Name": "lower non critical",
                    "Severity": 0,
                    "Value": 5
                },
                {
                    "Direction": "less than",
                    "Name": "lower critical",
                    "Severity": 1,
                    "Value": 0
                }
            ],
            "Type": "TMP75"
        }
    ],
    "Name": "WFP Baseboard",
    "Probe": "xyz.openbmc_project.FruDevice({'BOARD_PRODUCT_NAME' : '.*WFT'})"
}

Full Configuration

Configuration Records - Chassis Example

Although fan connectors are considered a part of a baseboard, the physical fans themselves are considered as a part of a chassis. In order for a fan to be matched with a fan connector, the keyword "Bind" is used. The example below shows how a chassis fan named "Fan 1" is connected to the connector named "1U System Fan connector 1". When the probe command finds the correct product name in baseboard FRU, the fan and the connector are considered as being joined together.

{
    "Exposes": [
        {
            "BindConnector": "1U System Fan connector 1",
            "Name": "Fan 1",
            "Thresholds": [
                {
                    "Direction": "less than",
                    "Name": "lower critical",
                    "Severity": 1,
                    "Value": 1750
                },
                {
                    "Direction": "less than",
                    "Name": "lower non critical",
                    "Severity": 0,
                    "Value": 2000
                }
            ],
            "Type": "AspeedFan"
        }
    ]
}

Enabling Sensors

As daemons can trigger off of shared types, sometimes some handshaking will be needed to enable sensors. Using the TMP75 sensor as an example, when the sensor object is enabled, the device tree must be updated before scanning may begin. The entity-manager can key off of different types and export devices for specific configurations. Once this is done, the baseboard temperature sensor daemon can scan the sensors.