pseq: Modify JSON parsing for entire file
Modify functions that parse the entire JSON configuration file for the
phosphor-power-sequencer application. Parse the new top level JSON
properties.
Tested:
* Ran automated tests
Change-Id: I08f20fc09aa4eff5bc819398834afac61054f8db
Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
diff --git a/phosphor-power-sequencer/src/config_file_parser.cpp b/phosphor-power-sequencer/src/config_file_parser.cpp
index 9e4a18d..44c7877 100644
--- a/phosphor-power-sequencer/src/config_file_parser.cpp
+++ b/phosphor-power-sequencer/src/config_file_parser.cpp
@@ -74,7 +74,8 @@
return pathName;
}
-std::vector<std::unique_ptr<Rail>> parse(const std::filesystem::path& pathName)
+std::vector<std::unique_ptr<Chassis>> parse(
+ const std::filesystem::path& pathName, Services& services)
{
try
{
@@ -83,7 +84,7 @@
json rootElement = json::parse(file);
// Parse tree of JSON elements and return corresponding C++ objects
- return internal::parseRoot(rootElement);
+ return internal::parseRoot(rootElement, services);
}
catch (const std::exception& e)
{
@@ -482,23 +483,37 @@
return rails;
}
-std::vector<std::unique_ptr<Rail>> parseRoot(const json& element)
+std::vector<std::unique_ptr<Chassis>> parseRoot(const json& element,
+ Services& services)
{
- std::map<std::string, std::string> variables{};
-
verifyIsObject(element);
unsigned int propertyCount{0};
- // Required rails property
- const json& railsElement = getRequiredProperty(element, "rails");
- std::vector<std::unique_ptr<Rail>> rails =
- parseRailArray(railsElement, variables);
+ // Optional comments property; value not stored
+ if (element.contains("comments"))
+ {
+ ++propertyCount;
+ }
+
+ // Optional chassis_templates property
+ std::map<std::string, JSONRefWrapper> chassisTemplates{};
+ auto chassisTemplatesIt = element.find("chassis_templates");
+ if (chassisTemplatesIt != element.end())
+ {
+ chassisTemplates = parseChassisTemplateArray(*chassisTemplatesIt);
+ ++propertyCount;
+ }
+
+ // Required chassis property
+ const json& chassisElement = getRequiredProperty(element, "chassis");
+ std::vector<std::unique_ptr<Chassis>> chassis =
+ parseChassisArray(chassisElement, chassisTemplates, services);
++propertyCount;
// Verify no invalid properties exist
verifyPropertyCount(element, propertyCount);
- return rails;
+ return chassis;
}
std::map<std::string, std::string> parseVariables(const json& element)
diff --git a/phosphor-power-sequencer/src/config_file_parser.hpp b/phosphor-power-sequencer/src/config_file_parser.hpp
index f43ad0e..b5b54fb 100644
--- a/phosphor-power-sequencer/src/config_file_parser.hpp
+++ b/phosphor-power-sequencer/src/config_file_parser.hpp
@@ -69,14 +69,16 @@
/**
* Parses the specified JSON configuration file.
*
- * Returns the corresponding C++ Rail objects.
+ * Returns the corresponding C++ Chassis objects.
*
* Throws a ConfigFileParserError if an error occurs.
*
* @param pathName configuration file path name
- * @return vector of Rail objects
+ * @param services system services like hardware presence and the journal
+ * @return vector of Chassis objects
*/
-std::vector<std::unique_ptr<Rail>> parse(const std::filesystem::path& pathName);
+std::vector<std::unique_ptr<Chassis>> parse(
+ const std::filesystem::path& pathName, Services& services);
/*
* Internal implementation details for parse()
@@ -270,14 +272,16 @@
/**
* Parses the JSON root element of the entire configuration file.
*
- * Returns the corresponding C++ Rail objects.
+ * Returns the corresponding C++ Chassis objects.
*
* Throws an exception if parsing fails.
*
* @param element JSON element
- * @return vector of Rail objects
+ * @param services system services like hardware presence and the journal
+ * @return vector of Chassis objects
*/
-std::vector<std::unique_ptr<Rail>> parseRoot(const json& element);
+std::vector<std::unique_ptr<Chassis>> parseRoot(const json& element,
+ Services& services);
/**
* Parses a JSON element containing an object with variable names and values.
diff --git a/phosphor-power-sequencer/src/power_control.cpp b/phosphor-power-sequencer/src/power_control.cpp
index fa43ff9..4b8315c 100644
--- a/phosphor-power-sequencer/src/power_control.cpp
+++ b/phosphor-power-sequencer/src/power_control.cpp
@@ -371,14 +371,14 @@
}
bool PowerControl::parseConfigFile(const std::filesystem::path& configFile,
- std::vector<std::unique_ptr<Rail>>& rails)
+ std::vector<std::unique_ptr<Rail>>&)
{
// Parse JSON configuration file
bool wasParsed{false};
try
{
- rails = config_file_parser::parse(configFile);
- wasParsed = true;
+ config_file_parser::parse(configFile, services);
+ // wasParsed = true;
}
catch (const std::exception& e)
{
diff --git a/phosphor-power-sequencer/test/config_file_parser_tests.cpp b/phosphor-power-sequencer/test/config_file_parser_tests.cpp
index 2fdf5af..78899bb 100644
--- a/phosphor-power-sequencer/test/config_file_parser_tests.cpp
+++ b/phosphor-power-sequencer/test/config_file_parser_tests.cpp
@@ -252,18 +252,18 @@
{
const json configFileContents = R"(
{
- "rails": [
- {
- "name": "VDD_CPU0",
- "page": 11,
- "check_status_vout": true
- },
- {
- "name": "VCS_CPU1",
- "presence": "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu1",
- "gpio": { "line": 60 }
- }
- ]
+ "chassis": [
+ {
+ "number": 1,
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis1",
+ "power_sequencers": []
+ },
+ {
+ "number": 2,
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis2",
+ "power_sequencers": []
+ }
+ ]
}
)"_json;
@@ -271,28 +271,36 @@
fs::path pathName{configFile.getPath()};
writeConfigFile(pathName, configFileContents);
- auto rails = parse(pathName);
+ MockServices services{};
+ auto chassis = parse(pathName, services);
- EXPECT_EQ(rails.size(), 2);
- EXPECT_EQ(rails[0]->getName(), "VDD_CPU0");
- EXPECT_EQ(rails[1]->getName(), "VCS_CPU1");
+ EXPECT_EQ(chassis.size(), 2);
+ EXPECT_EQ(chassis[0]->getNumber(), 1);
+ EXPECT_EQ(chassis[0]->getInventoryPath(),
+ "/xyz/openbmc_project/inventory/system/chassis1");
+ EXPECT_EQ(chassis[1]->getNumber(), 2);
+ EXPECT_EQ(chassis[1]->getInventoryPath(),
+ "/xyz/openbmc_project/inventory/system/chassis2");
}
// Test where fails: File does not exist
{
fs::path pathName{"/tmp/non_existent_file"};
- EXPECT_THROW(parse(pathName), ConfigFileParserError);
+ MockServices services{};
+ EXPECT_THROW(parse(pathName, services), ConfigFileParserError);
}
// Test where fails: File is not readable
{
const json configFileContents = R"(
{
- "rails": [
- {
- "name": "VDD_CPU0"
- }
- ]
+ "chassis": [
+ {
+ "number": 1,
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis1",
+ "power_sequencers": []
+ }
+ ]
}
)"_json;
@@ -301,7 +309,8 @@
writeConfigFile(pathName, configFileContents);
chmod(pathName.c_str(), 0222);
- EXPECT_THROW(parse(pathName), ConfigFileParserError);
+ MockServices services{};
+ EXPECT_THROW(parse(pathName, services), ConfigFileParserError);
}
// Test where fails: File is not valid JSON
@@ -312,7 +321,8 @@
fs::path pathName{configFile.getPath()};
writeConfigFile(pathName, configFileContents);
- EXPECT_THROW(parse(pathName), ConfigFileParserError);
+ MockServices services{};
+ EXPECT_THROW(parse(pathName, services), ConfigFileParserError);
}
// Test where fails: JSON does not conform to config file format
@@ -323,7 +333,8 @@
fs::path pathName{configFile.getPath()};
writeConfigFile(pathName, configFileContents);
- EXPECT_THROW(parse(pathName), ConfigFileParserError);
+ MockServices services{};
+ EXPECT_THROW(parse(pathName, services), ConfigFileParserError);
}
}
@@ -2516,35 +2527,83 @@
TEST(ConfigFileParserTests, ParseRoot)
{
- // Test where works
+ // Test where works: Only required properties specified
{
const json element = R"(
{
- "rails": [
- {
- "name": "VDD_CPU0",
- "page": 11,
- "check_status_vout": true
- },
- {
- "name": "VCS_CPU1",
- "presence": "/xyz/openbmc_project/inventory/system/chassis/motherboard/cpu1",
- "gpio": { "line": 60 }
- }
- ]
+ "chassis": [
+ {
+ "number": 1,
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis1",
+ "power_sequencers": []
+ },
+ {
+ "number": 2,
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis2",
+ "power_sequencers": []
+ }
+ ]
}
)"_json;
- auto rails = parseRoot(element);
- EXPECT_EQ(rails.size(), 2);
- EXPECT_EQ(rails[0]->getName(), "VDD_CPU0");
- EXPECT_EQ(rails[1]->getName(), "VCS_CPU1");
+ MockServices services{};
+ auto chassis = parseRoot(element, services);
+ EXPECT_EQ(chassis.size(), 2);
+ EXPECT_EQ(chassis[0]->getNumber(), 1);
+ EXPECT_EQ(chassis[0]->getInventoryPath(),
+ "/xyz/openbmc_project/inventory/system/chassis1");
+ EXPECT_EQ(chassis[1]->getNumber(), 2);
+ EXPECT_EQ(chassis[1]->getInventoryPath(),
+ "/xyz/openbmc_project/inventory/system/chassis2");
+ }
+
+ // Test where works: All properties specified
+ {
+ const json element = R"(
+ {
+ "comments": [ "Config file for a FooBar one-chassis system" ],
+ "chassis_templates": [
+ {
+ "id": "foo_chassis",
+ "number": "${chassis_number}",
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}",
+ "power_sequencers": []
+ },
+ {
+ "id": "bar_chassis",
+ "number": "${chassis_number}",
+ "inventory_path": "/xyz/openbmc_project/inventory/system/bar_chassis${chassis_number}",
+ "power_sequencers": []
+ }
+ ],
+ "chassis": [
+ {
+ "template_id": "foo_chassis",
+ "template_variable_values": { "chassis_number": "2" }
+ },
+ {
+ "template_id": "bar_chassis",
+ "template_variable_values": { "chassis_number": "3" }
+ }
+ ]
+ }
+ )"_json;
+ MockServices services{};
+ auto chassis = parseRoot(element, services);
+ EXPECT_EQ(chassis.size(), 2);
+ EXPECT_EQ(chassis[0]->getNumber(), 2);
+ EXPECT_EQ(chassis[0]->getInventoryPath(),
+ "/xyz/openbmc_project/inventory/system/chassis2");
+ EXPECT_EQ(chassis[1]->getNumber(), 3);
+ EXPECT_EQ(chassis[1]->getInventoryPath(),
+ "/xyz/openbmc_project/inventory/system/bar_chassis3");
}
// Test where fails: Element is not an object
try
{
const json element = R"( [ "VDD_CPU0", "VCS_CPU1" ] )"_json;
- parseRoot(element);
+ MockServices services{};
+ parseRoot(element, services);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const std::invalid_argument& e)
@@ -2552,30 +2611,48 @@
EXPECT_STREQ(e.what(), "Element is not an object");
}
- // Test where fails: Required rails property not specified
+ // Test where fails: Required chassis property not specified
try
{
const json element = R"(
{
+ "comments": [ "Config file for a FooBar one-chassis system" ],
+ "chassis_templates": [
+ {
+ "id": "foo_chassis",
+ "number": "${chassis_number}",
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}",
+ "power_sequencers": []
+ }
+ ]
}
)"_json;
- parseRoot(element);
+ MockServices services{};
+ parseRoot(element, services);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const std::invalid_argument& e)
{
- EXPECT_STREQ(e.what(), "Required property missing: rails");
+ EXPECT_STREQ(e.what(), "Required property missing: chassis");
}
- // Test where fails: rails value is invalid
+ // Test where fails: chassis_templates value is invalid
try
{
const json element = R"(
{
- "rails": 31
+ "chassis_templates": true,
+ "chassis": [
+ {
+ "number": 1,
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis1",
+ "power_sequencers": []
+ }
+ ]
}
)"_json;
- parseRoot(element);
+ MockServices services{};
+ parseRoot(element, services);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const std::invalid_argument& e)
@@ -2583,22 +2660,46 @@
EXPECT_STREQ(e.what(), "Element is not an array");
}
+ // Test where fails: chassis value is invalid
+ try
+ {
+ const json element = R"(
+ {
+ "chassis": [
+ {
+ "number": "one",
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis1",
+ "power_sequencers": []
+ }
+ ]
+ }
+ )"_json;
+ MockServices services{};
+ parseRoot(element, services);
+ ADD_FAILURE() << "Should not have reached this line.";
+ }
+ catch (const std::invalid_argument& e)
+ {
+ EXPECT_STREQ(e.what(), "Element is not an integer");
+ }
+
// Test where fails: Invalid property specified
try
{
const json element = R"(
{
- "rails": [
- {
- "name": "VDD_CPU0",
- "page": 11,
- "check_status_vout": true
- }
- ],
- "foo": true
+ "chassis": [
+ {
+ "number": 1,
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis1",
+ "power_sequencers": []
+ }
+ ],
+ "foo": true
}
)"_json;
- parseRoot(element);
+ MockServices services{};
+ parseRoot(element, services);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const std::invalid_argument& e)