diff --git a/CMakeLists.txt b/CMakeLists.txt index a60d7f0bf6d90..f44708c8638aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -173,6 +173,7 @@ find_package(fizz CONFIG REQUIRED) find_package(fmt CONFIG REQUIRED) find_package(wangle CONFIG REQUIRED) find_package(FBThrift CONFIG REQUIRED) +find_package(range-v3 REQUIRED) include_directories(${FB303_INCLUDE_DIR}) find_path(FATAL_INCLUDE_DIR NAMES fatal/portability.h) include_directories(${FATAL_INCLUDE_DIR}) diff --git a/build/fbcode_builder/manifests/fboss b/build/fbcode_builder/manifests/fboss index ce9b36109e22e..79ce3fb9e7cad 100644 --- a/build/fbcode_builder/manifests/fboss +++ b/build/fbcode_builder/manifests/fboss @@ -39,6 +39,7 @@ CLI11 exprtk nlohmann-json libgpiod +range-v3 [shipit.pathmap] fbcode/fboss/github = . diff --git a/cmake/PlatformPlatformManager.cmake b/cmake/PlatformPlatformManager.cmake index 4b9f75399d265..2cc1190f81e68 100644 --- a/cmake/PlatformPlatformManager.cmake +++ b/cmake/PlatformPlatformManager.cmake @@ -140,6 +140,7 @@ target_link_libraries(platform_manager_config_validator platform_manager_i2c_explorer platform_manager_config_cpp2 Folly::folly + range-v3 ) add_executable(platform_manager diff --git a/fboss/platform/platform_manager/BUCK b/fboss/platform/platform_manager/BUCK index 82f42f0309cc3..e46121fb05f9a 100644 --- a/fboss/platform/platform_manager/BUCK +++ b/fboss/platform/platform_manager/BUCK @@ -46,8 +46,11 @@ cpp_library( "ConfigValidator.cpp", ], exported_deps = [ + "fbsource//third-party/range-v3:range-v3", ":i2c_misc", ":platform_manager_config-cpp2-types", + "//folly/gen:base", + "//folly/gen:string", "//folly/logging:logging", ], exported_external_deps = [ diff --git a/fboss/platform/platform_manager/ConfigValidator.cpp b/fboss/platform/platform_manager/ConfigValidator.cpp index badfdb217771b..9974c3669df79 100644 --- a/fboss/platform/platform_manager/ConfigValidator.cpp +++ b/fboss/platform/platform_manager/ConfigValidator.cpp @@ -2,7 +2,16 @@ #include "fboss/platform/platform_manager/ConfigValidator.h" +#include +#include #include +#include +#include +#include +#include +#include +#include +#include #include #include "fboss/platform/platform_manager/I2cAddr.h" @@ -12,7 +21,8 @@ const re2::RE2 kRpmVersionRegex{"^[0-9]+\\.[0-9]+\\.[0-9]+\\-[0-9]+$"}; const re2::RE2 kPciIdRegex{"0x[0-9a-f]{4}"}; const re2::RE2 kPciDevOffsetRegex{"0x[0-9a-f]+"}; const re2::RE2 kSymlinkRegex{"^/run/devmap/(?P[a-z0-9-]+)/.+"}; -const re2::RE2 kDevPathRegex{"/([A-Z]+(_[A-Z]+)*_SLOT@[0-9]+/)*\\[.+\\]"}; +const re2::RE2 kDevPathRegex{"(?P.*)\\[(?P.+)\\]"}; +const re2::RE2 kSlotPathRegex{"/|(/([A-Z]+_)+SLOT@\\d+)+"}; const re2::RE2 kInfoRomDevicePrefixRegex{"^fpga_info_(dom|iob|scm|mcb)$"}; constexpr auto kSymlinkDirs = { "eeproms", @@ -234,11 +244,75 @@ bool ConfigValidator::isValidSymlink(const std::string& symlink) { return true; } -bool ConfigValidator::isValidDevicePath(const std::string& devicePath) { - if (!re2::RE2::FullMatch(devicePath, kDevPathRegex)) { +bool ConfigValidator::isValidDevicePath( + const PlatformConfig& platformConfig, + const std::string& devicePath) { + std::string slotPath, deviceName; + if (!re2::RE2::FullMatch(devicePath, kDevPathRegex, &slotPath, &deviceName)) { XLOG(ERR) << fmt::format("Invalid device path {}", devicePath); return false; } + CHECK_EQ(slotPath.back(), '/'); + if (slotPath.length() > 1) { + slotPath.pop_back(); + } + if (!isValidSlotPath(platformConfig, slotPath)) { + return false; + } + return true; +} + +bool ConfigValidator::isValidSlotPath( + const PlatformConfig& platformConfig, + const std::string& slotPath) { + // Syntactic validation. + if (!re2::RE2::FullMatch(slotPath, kSlotPathRegex)) { + XLOG(ERR) << fmt::format("Invalid slot path {}", slotPath); + return false; + } + + // Slot topological validation. + // Starting from the root, check for a PmUnit from CurrSlot to NextSlot. + for (auto currSlotType{*platformConfig.rootSlotType()}; + const auto& nextSlotName : slotPath | ranges::views::split('/') | + ranges::views::drop(1) | ranges::to>) { + // Find all pmUnits that can be plug into currSlotType. + std::vector pmUnitConfigs = *platformConfig.pmUnitConfigs() | + ranges::views::values | + ranges::views::filter([&](const auto& pmUnitConfig) { + return *pmUnitConfig.pluggedInSlotType() == currSlotType; + }) | + ranges::to_vector; + if (pmUnitConfigs.empty()) { + XLOG(ERR) << fmt::format( + "Couldn't find PmUnitConfigs that can be plug-into {} in SlotPath {}", + currSlotType, + slotPath); + return false; + } + // Find PmUnits' outgoingSlotConfig of nextSlotName and their SlotType. + auto nextSlotType = + pmUnitConfigs | ranges::views::move | + ranges::views::filter([&](const auto& pmUnitConfig) { + return pmUnitConfig.outgoingSlotConfigs()->contains(nextSlotName); + }) | + ranges::views::transform([&](const auto& pmUnitConfig) -> SlotType { + return *pmUnitConfig.outgoingSlotConfigs() + ->at(nextSlotName) + .slotType(); + }) | + ranges::views::unique | ranges::to_vector; + if (nextSlotType.size() != 1) { + XLOG(ERR) << fmt::format( + "Invalid SlotName {} in SlotPath {}. Maps to {} SlotConfig(s)", + nextSlotName, + slotPath, + nextSlotType.size()); + return false; + } + // Set currSlotType as the nextSlotType + currSlotType = nextSlotType.front(); + } return true; } @@ -309,7 +383,7 @@ bool ConfigValidator::isValid(const PlatformConfig& config) { // Validate symbolic links for (const auto& [symlink, devicePath] : *config.symbolicLinkToDevicePath()) { - if (!isValidSymlink(symlink) || !isValidDevicePath(devicePath)) { + if (!isValidSymlink(symlink) || !isValidDevicePath(config, devicePath)) { return false; } } diff --git a/fboss/platform/platform_manager/ConfigValidator.h b/fboss/platform/platform_manager/ConfigValidator.h index 5b420236166ca..9535f27dd8581 100644 --- a/fboss/platform/platform_manager/ConfigValidator.h +++ b/fboss/platform/platform_manager/ConfigValidator.h @@ -17,7 +17,12 @@ class ConfigValidator { bool isValidFpgaIpBlockConfig(const FpgaIpBlockConfig& fpgaIpBlockConfig); bool isValidPciDeviceConfig(const PciDeviceConfig& pciDeviceConfig); bool isValidI2cDeviceConfig(const I2cDeviceConfig& i2cDeviceConfig); - bool isValidDevicePath(const std::string& devicePath); + bool isValidDevicePath( + const PlatformConfig& platformConfig, + const std::string& devicePath); + bool isValidSlotPath( + const PlatformConfig& platformConfig, + const std::string& slotPath); bool isValidSymlink(const std::string& symlink); bool isValidPresenceDetection(const PresenceDetection& presenceDetection); bool isValidSpiDeviceConfigs( diff --git a/fboss/platform/platform_manager/Utils.cpp b/fboss/platform/platform_manager/Utils.cpp index f132be64b6f57..acc2c68da6ebe 100644 --- a/fboss/platform/platform_manager/Utils.cpp +++ b/fboss/platform/platform_manager/Utils.cpp @@ -90,9 +90,6 @@ PlatformConfig Utils::getConfig() { std::pair Utils::parseDevicePath( const std::string& devicePath) { - if (!ConfigValidator().isValidDevicePath(devicePath)) { - throw std::runtime_error(fmt::format("Invalid DevicePath {}", devicePath)); - } std::string slotPath, deviceName; CHECK(RE2::FullMatch(devicePath, kPmDeviceParseRe, &slotPath, &deviceName)); // Remove trailling '/' (e.g /abc/dfg/) diff --git a/fboss/platform/platform_manager/tests/ConfigValidatorTest.cpp b/fboss/platform/platform_manager/tests/ConfigValidatorTest.cpp index 96c700ad311a6..63c35362f3e9b 100644 --- a/fboss/platform/platform_manager/tests/ConfigValidatorTest.cpp +++ b/fboss/platform/platform_manager/tests/ConfigValidatorTest.cpp @@ -310,18 +310,43 @@ TEST(ConfigValidatorTest, Symlink) { } TEST(ConfigValidatorTest, DevicePath) { - EXPECT_FALSE(ConfigValidator().isValidDevicePath("/[]")); - EXPECT_FALSE(ConfigValidator().isValidDevicePath("/SCM_SLOT@1/[a")); - EXPECT_FALSE(ConfigValidator().isValidDevicePath("/SMB_SLOT@1/SCM_SLOT/[a]")); - EXPECT_TRUE(ConfigValidator().isValidDevicePath("/[chassis_eeprom]")); - EXPECT_TRUE(ConfigValidator().isValidDevicePath("/SMB_SLOT@0/[MCB_MUX_1]")); - EXPECT_TRUE( - ConfigValidator().isValidDevicePath("/SMB_SLOT@0/SCM_SLOT@1/[sensor]")); - EXPECT_TRUE(ConfigValidator().isValidDevicePath("/SMB_FRU_SLOT@0/[idprom]")); + PlatformConfig config; + // Syntatically incorrect + EXPECT_FALSE(ConfigValidator().isValidDevicePath(config, "/SCM_SLOT@1/[a")); + EXPECT_FALSE( + ConfigValidator().isValidDevicePath(config, "/SMB_SLOT@1/SCM_SLOT/[a]")); + EXPECT_FALSE(ConfigValidator().isValidDevicePath(config, "/[]")); + EXPECT_FALSE( + ConfigValidator().isValidDevicePath(config, "/COME@SCM_SLOT@0/[idprom]")); + EXPECT_FALSE(ConfigValidator().isValidDevicePath( + config, "/COME_SLOT@SCM_SLOT@0/[SCM_MUX_5]")); + // Topologically incorrect. + config.rootSlotType() = "MCB_SLOT"; + PmUnitConfig mcb, jumper, scm; + mcb.pluggedInSlotType() = "MCB_SLOT"; + jumper.pluggedInSlotType() = "JUMPER_SLOT"; + scm.pluggedInSlotType() = "SCM_SLOT"; + SlotConfig jumperSlotConfig, scmSlotConfig; + jumperSlotConfig.slotType() = "JUMPER_SLOT"; + scmSlotConfig.slotType() = "SCM_SLOT"; + mcb.outgoingSlotConfigs() = { + {"JUMPER_SLOT@0", jumperSlotConfig}, {"JUMPER_SLOT@1", jumperSlotConfig}}; + jumper.outgoingSlotConfigs() = {{"SCM_SLOT@0", scmSlotConfig}}; + config.pmUnitConfigs() = {{"MCB", mcb}, {"JUMPER", jumper}, {"SCM", scm}}; EXPECT_FALSE( - ConfigValidator().isValidDevicePath("/COME@SCM_SLOT@0/[idprom]")); + ConfigValidator().isValidDevicePath(config, "/SMB_SLOT@0/[sensor1]")); EXPECT_FALSE( - ConfigValidator().isValidDevicePath("/COME_SLOT@SCM_SLOT@0/[SCM_MUX_5]")); + ConfigValidator().isValidDevicePath(config, "/JUMPER_SLOT@2/[fpga]")); + EXPECT_FALSE(ConfigValidator().isValidDevicePath( + config, "/JUMPER_SLOT@0/SCM_SLOT@1/[IDPROM]")); + // Correct. + EXPECT_TRUE(ConfigValidator().isValidDevicePath(config, "/[chassis_eeprom]")); + EXPECT_TRUE( + ConfigValidator().isValidDevicePath(config, "/JUMPER_SLOT@0/[sensor]")); + EXPECT_TRUE( + ConfigValidator().isValidDevicePath(config, "/JUMPER_SLOT@1/[sensor]")); + EXPECT_TRUE(ConfigValidator().isValidDevicePath( + config, "/JUMPER_SLOT@0/SCM_SLOT@0/[IDPROM]")); } TEST(ConfigValidatorTest, BspRpm) { diff --git a/fboss/platform/platform_manager/tests/UtilsTest.cpp b/fboss/platform/platform_manager/tests/UtilsTest.cpp index 9ef9358d0d56c..e7176cca9ece5 100644 --- a/fboss/platform/platform_manager/tests/UtilsTest.cpp +++ b/fboss/platform/platform_manager/tests/UtilsTest.cpp @@ -1,7 +1,5 @@ // (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary. -#include - #include #include "fboss/platform/platform_manager/Utils.h" @@ -27,7 +25,7 @@ TEST(UtilsTest, ParseDevicePath) { EXPECT_EQ( makeDevicePathPair("/MCB_SLOT@0/SMB_SLOT@11", "SMB_IOB_I2C_1"), Utils().parseDevicePath("/MCB_SLOT@0/SMB_SLOT@11/[SMB_IOB_I2C_1]")); - EXPECT_THROW(Utils().parseDevicePath("ABCDE/[abc]"), std::runtime_error); - EXPECT_THROW(Utils().parseDevicePath("/MCB_SLOT/[abc]"), std::runtime_error); - EXPECT_THROW(Utils().parseDevicePath("/MCB_SLOT@1/[]"), std::runtime_error); + EXPECT_NO_THROW(Utils().parseDevicePath("ABCDE/[abc]")); + EXPECT_NO_THROW(Utils().parseDevicePath("/MCB_SLOT/[abc]")); + EXPECT_NO_THROW(Utils().parseDevicePath("/MCB_SLOT@1/[]")); }