Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SlotPath topology config validation. #285

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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})
Expand Down
1 change: 1 addition & 0 deletions build/fbcode_builder/manifests/fboss
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ CLI11
exprtk
nlohmann-json
libgpiod
range-v3

[shipit.pathmap]
fbcode/fboss/github = .
Expand Down
1 change: 1 addition & 0 deletions cmake/PlatformPlatformManager.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions fboss/platform/platform_manager/BUCK
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down
82 changes: 78 additions & 4 deletions fboss/platform/platform_manager/ConfigValidator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,16 @@

#include "fboss/platform/platform_manager/ConfigValidator.h"

#include <folly/gen/Base.h>
#include <folly/gen/String.h>
#include <folly/logging/xlog.h>
#include <range/v3/range/conversion.hpp>
#include <range/v3/view/drop.hpp>
#include <range/v3/view/map.hpp>
#include <range/v3/view/move.hpp>
#include <range/v3/view/split.hpp>
#include <range/v3/view/transform.hpp>
#include <range/v3/view/unique.hpp>
#include <re2/re2.h>

#include "fboss/platform/platform_manager/I2cAddr.h"
Expand All @@ -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<SymlinkDirs>[a-z0-9-]+)/.+"};
const re2::RE2 kDevPathRegex{"/([A-Z]+(_[A-Z]+)*_SLOT@[0-9]+/)*\\[.+\\]"};
const re2::RE2 kDevPathRegex{"(?P<SlotPath>.*)\\[(?P<DeviceName>.+)\\]"};
const re2::RE2 kSlotPathRegex{"/|(/([A-Z]+_)+SLOT@\\d+)+"};
const re2::RE2 kInfoRomDevicePrefixRegex{"^fpga_info_(dom|iob|scm|mcb)$"};
constexpr auto kSymlinkDirs = {
"eeproms",
Expand Down Expand Up @@ -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<std::vector<std::string>>) {
// Find all pmUnits that can be plug into currSlotType.
std::vector<PmUnitConfig> 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;
}

Expand Down Expand Up @@ -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;
}
}
Expand Down
7 changes: 6 additions & 1 deletion fboss/platform/platform_manager/ConfigValidator.h
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
3 changes: 0 additions & 3 deletions fboss/platform/platform_manager/Utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,6 @@ PlatformConfig Utils::getConfig() {

std::pair<std::string, std::string> 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/)
Expand Down
45 changes: 35 additions & 10 deletions fboss/platform/platform_manager/tests/ConfigValidatorTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
8 changes: 3 additions & 5 deletions fboss/platform/platform_manager/tests/UtilsTest.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary.

#include <stdexcept>

#include <gtest/gtest.h>

#include "fboss/platform/platform_manager/Utils.h"
Expand All @@ -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/[]"));
}
Loading