pseq: Add variable support to config file parser
Add template variable support to the functions that parse the
phosphor-power-sequencer configuration file.
Tested:
* Ran automated tests.
Change-Id: I649d66de520e9138fc3a5dc34de61cc4cb03fe53
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 3163aff..0812744 100644
--- a/phosphor-power-sequencer/src/config_file_parser.cpp
+++ b/phosphor-power-sequencer/src/config_file_parser.cpp
@@ -93,14 +93,15 @@
namespace internal
{
-GPIO parseGPIO(const json& element)
+GPIO parseGPIO(const json& element,
+ const std::map<std::string, std::string>& variables)
{
verifyIsObject(element);
unsigned int propertyCount{0};
// Required line property
const json& lineElement = getRequiredProperty(element, "line");
- unsigned int line = parseUnsignedInteger(lineElement);
+ unsigned int line = parseUnsignedInteger(lineElement, variables);
++propertyCount;
// Optional active_low property
@@ -108,7 +109,7 @@
auto activeLowIt = element.find("active_low");
if (activeLowIt != element.end())
{
- activeLow = parseBoolean(*activeLowIt);
+ activeLow = parseBoolean(*activeLowIt, variables);
++propertyCount;
}
@@ -141,14 +142,15 @@
return {bus, address};
}
-std::unique_ptr<Rail> parseRail(const json& element)
+std::unique_ptr<Rail> parseRail(
+ const json& element, const std::map<std::string, std::string>& variables)
{
verifyIsObject(element);
unsigned int propertyCount{0};
// Required name property
const json& nameElement = getRequiredProperty(element, "name");
- std::string name = parseString(nameElement);
+ std::string name = parseString(nameElement, false, variables);
++propertyCount;
// Optional presence property
@@ -156,7 +158,7 @@
auto presenceIt = element.find("presence");
if (presenceIt != element.end())
{
- presence = parseString(*presenceIt);
+ presence = parseString(*presenceIt, false, variables);
++propertyCount;
}
@@ -165,7 +167,7 @@
auto pageIt = element.find("page");
if (pageIt != element.end())
{
- page = parseUint8(*pageIt);
+ page = parseUint8(*pageIt, variables);
++propertyCount;
}
@@ -174,7 +176,7 @@
auto isPowerSupplyRailIt = element.find("is_power_supply_rail");
if (isPowerSupplyRailIt != element.end())
{
- isPowerSupplyRail = parseBoolean(*isPowerSupplyRailIt);
+ isPowerSupplyRail = parseBoolean(*isPowerSupplyRailIt, variables);
++propertyCount;
}
@@ -183,7 +185,7 @@
auto checkStatusVoutIt = element.find("check_status_vout");
if (checkStatusVoutIt != element.end())
{
- checkStatusVout = parseBoolean(*checkStatusVoutIt);
+ checkStatusVout = parseBoolean(*checkStatusVoutIt, variables);
++propertyCount;
}
@@ -192,7 +194,8 @@
auto compareVoltageToLimitIt = element.find("compare_voltage_to_limit");
if (compareVoltageToLimitIt != element.end())
{
- compareVoltageToLimit = parseBoolean(*compareVoltageToLimitIt);
+ compareVoltageToLimit =
+ parseBoolean(*compareVoltageToLimitIt, variables);
++propertyCount;
}
@@ -201,7 +204,7 @@
auto gpioIt = element.find("gpio");
if (gpioIt != element.end())
{
- gpio = parseGPIO(*gpioIt);
+ gpio = parseGPIO(*gpioIt, variables);
++propertyCount;
}
@@ -219,25 +222,29 @@
checkStatusVout, compareVoltageToLimit, gpio);
}
-std::vector<std::unique_ptr<Rail>> parseRailArray(const json& element)
+std::vector<std::unique_ptr<Rail>> parseRailArray(
+ const json& element, const std::map<std::string, std::string>& variables)
{
verifyIsArray(element);
std::vector<std::unique_ptr<Rail>> rails;
for (auto& railElement : element)
{
- rails.emplace_back(parseRail(railElement));
+ rails.emplace_back(parseRail(railElement, variables));
}
return rails;
}
std::vector<std::unique_ptr<Rail>> parseRoot(const json& element)
{
+ 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);
+ std::vector<std::unique_ptr<Rail>> rails =
+ parseRailArray(railsElement, variables);
++propertyCount;
// Verify no invalid properties exist
diff --git a/phosphor-power-sequencer/src/config_file_parser.hpp b/phosphor-power-sequencer/src/config_file_parser.hpp
index a57083e..0f5fe3c 100644
--- a/phosphor-power-sequencer/src/config_file_parser.hpp
+++ b/phosphor-power-sequencer/src/config_file_parser.hpp
@@ -86,9 +86,11 @@
* Throws an exception if parsing fails.
*
* @param element JSON element
+ * @param variables variables map used to expand variables in element value
* @return GPIO object
*/
-GPIO parseGPIO(const nlohmann::json& element);
+GPIO parseGPIO(const nlohmann::json& element,
+ const std::map<std::string, std::string>& variables);
/**
* Parses a JSON element containing an i2c_interface object.
@@ -113,9 +115,12 @@
* Throws an exception if parsing fails.
*
* @param element JSON element
+ * @param variables variables map used to expand variables in element value
* @return Rail object
*/
-std::unique_ptr<Rail> parseRail(const nlohmann::json& element);
+std::unique_ptr<Rail> parseRail(
+ const nlohmann::json& element,
+ const std::map<std::string, std::string>& variables);
/**
* Parses a JSON element containing an array of rails.
@@ -125,10 +130,12 @@
* Throws an exception if parsing fails.
*
* @param element JSON element
+ * @param variables variables map used to expand variables in element value
* @return vector of Rail objects
*/
std::vector<std::unique_ptr<Rail>> parseRailArray(
- const nlohmann::json& element);
+ const nlohmann::json& element,
+ const std::map<std::string, std::string>& variables);
/**
* Parses the JSON root element of the entire configuration file.
diff --git a/phosphor-power-sequencer/test/config_file_parser_tests.cpp b/phosphor-power-sequencer/test/config_file_parser_tests.cpp
index 0fbf5fe..c6101d3 100644
--- a/phosphor-power-sequencer/test/config_file_parser_tests.cpp
+++ b/phosphor-power-sequencer/test/config_file_parser_tests.cpp
@@ -268,7 +268,7 @@
fs::path pathName{configFile.getPath()};
writeConfigFile(pathName, configFileContents);
- std::vector<std::unique_ptr<Rail>> rails = parse(pathName);
+ auto rails = parse(pathName);
EXPECT_EQ(rails.size(), 2);
EXPECT_EQ(rails[0]->getName(), "VDD_CPU0");
@@ -333,7 +333,8 @@
"line": 60
}
)"_json;
- GPIO gpio = parseGPIO(element);
+ std::map<std::string, std::string> variables{};
+ auto gpio = parseGPIO(element, variables);
EXPECT_EQ(gpio.line, 60);
EXPECT_FALSE(gpio.activeLow);
}
@@ -346,16 +347,33 @@
"active_low": true
}
)"_json;
- GPIO gpio = parseGPIO(element);
+ std::map<std::string, std::string> variables{};
+ auto gpio = parseGPIO(element, variables);
EXPECT_EQ(gpio.line, 131);
EXPECT_TRUE(gpio.activeLow);
}
+ // Test where works: Variables specified
+ {
+ const json element = R"(
+ {
+ "line": "${line}",
+ "active_low": "${active_low}"
+ }
+ )"_json;
+ std::map<std::string, std::string> variables{{"line", "54"},
+ {"active_low", "false"}};
+ auto gpio = parseGPIO(element, variables);
+ EXPECT_EQ(gpio.line, 54);
+ EXPECT_FALSE(gpio.activeLow);
+ }
+
// Test where fails: Element is not an object
try
{
const json element = R"( [ "vdda", "vddb" ] )"_json;
- parseGPIO(element);
+ std::map<std::string, std::string> variables{};
+ parseGPIO(element, variables);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const std::invalid_argument& e)
@@ -371,7 +389,8 @@
"active_low": true
}
)"_json;
- parseGPIO(element);
+ std::map<std::string, std::string> variables{};
+ parseGPIO(element, variables);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const std::invalid_argument& e)
@@ -388,7 +407,8 @@
"active_low": true
}
)"_json;
- parseGPIO(element);
+ std::map<std::string, std::string> variables{};
+ parseGPIO(element, variables);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const std::invalid_argument& e)
@@ -405,7 +425,8 @@
"active_low": "true"
}
)"_json;
- parseGPIO(element);
+ std::map<std::string, std::string> variables{};
+ parseGPIO(element, variables);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const std::invalid_argument& e)
@@ -422,13 +443,33 @@
"foo": "bar"
}
)"_json;
- parseGPIO(element);
+ std::map<std::string, std::string> variables{};
+ parseGPIO(element, variables);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const std::invalid_argument& e)
{
EXPECT_STREQ(e.what(), "Element contains an invalid property");
}
+
+ // Test where fails: Invalid variable value specified
+ try
+ {
+ const json element = R"(
+ {
+ "line": "${line}",
+ "active_low": "${active_low}"
+ }
+ )"_json;
+ std::map<std::string, std::string> variables{{"line", "-1"},
+ {"active_low", "false"}};
+ parseGPIO(element, variables);
+ ADD_FAILURE() << "Should not have reached this line.";
+ }
+ catch (const std::invalid_argument& e)
+ {
+ EXPECT_STREQ(e.what(), "Element is not an unsigned integer");
+ }
}
TEST(ConfigFileParserTests, ParseI2CInterface)
@@ -593,7 +634,8 @@
"name": "VDD_CPU0"
}
)"_json;
- std::unique_ptr<Rail> rail = parseRail(element);
+ std::map<std::string, std::string> variables{};
+ auto rail = parseRail(element, variables);
EXPECT_EQ(rail->getName(), "VDD_CPU0");
EXPECT_FALSE(rail->getPresence().has_value());
EXPECT_FALSE(rail->getPage().has_value());
@@ -616,7 +658,8 @@
"gpio": { "line": 60, "active_low": true }
}
)"_json;
- std::unique_ptr<Rail> rail = parseRail(element);
+ std::map<std::string, std::string> variables{};
+ auto rail = parseRail(element, variables);
EXPECT_EQ(rail->getName(), "12.0VB");
EXPECT_TRUE(rail->getPresence().has_value());
EXPECT_EQ(rail->getPresence().value(),
@@ -631,11 +674,49 @@
EXPECT_TRUE(rail->getGPIO().value().activeLow);
}
+ // Test where works: Variables specified
+ {
+ const json element = R"(
+ {
+ "name": "${name}",
+ "presence": "${presence}",
+ "page": "${page}",
+ "is_power_supply_rail": "${is_power_supply_rail}",
+ "check_status_vout": "${check_status_vout}",
+ "compare_voltage_to_limit": "${compare_voltage_to_limit}",
+ "gpio": { "line": "${line}", "active_low": true }
+ }
+ )"_json;
+ std::map<std::string, std::string> variables{
+ {"name", "vdd"},
+ {"presence",
+ "/xyz/openbmc_project/inventory/system/chassis/powersupply2"},
+ {"page", "9"},
+ {"is_power_supply_rail", "true"},
+ {"check_status_vout", "false"},
+ {"compare_voltage_to_limit", "true"},
+ {"line", "72"}};
+ auto rail = parseRail(element, variables);
+ EXPECT_EQ(rail->getName(), "vdd");
+ EXPECT_TRUE(rail->getPresence().has_value());
+ EXPECT_EQ(rail->getPresence().value(),
+ "/xyz/openbmc_project/inventory/system/chassis/powersupply2");
+ EXPECT_TRUE(rail->getPage().has_value());
+ EXPECT_EQ(rail->getPage().value(), 9);
+ EXPECT_TRUE(rail->isPowerSupplyRail());
+ EXPECT_FALSE(rail->getCheckStatusVout());
+ EXPECT_TRUE(rail->getCompareVoltageToLimit());
+ EXPECT_TRUE(rail->getGPIO().has_value());
+ EXPECT_EQ(rail->getGPIO().value().line, 72);
+ EXPECT_TRUE(rail->getGPIO().value().activeLow);
+ }
+
// Test where fails: Element is not an object
try
{
const json element = R"( [ "vdda", "vddb" ] )"_json;
- parseRail(element);
+ std::map<std::string, std::string> variables{};
+ parseRail(element, variables);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const std::invalid_argument& e)
@@ -651,7 +732,8 @@
"page": 11
}
)"_json;
- parseRail(element);
+ std::map<std::string, std::string> variables{};
+ parseRail(element, variables);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const std::invalid_argument& e)
@@ -668,7 +750,8 @@
"page": 11
}
)"_json;
- parseRail(element);
+ std::map<std::string, std::string> variables{};
+ parseRail(element, variables);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const std::invalid_argument& e)
@@ -685,7 +768,8 @@
"presence": false
}
)"_json;
- parseRail(element);
+ std::map<std::string, std::string> variables{};
+ parseRail(element, variables);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const std::invalid_argument& e)
@@ -702,7 +786,8 @@
"page": 256
}
)"_json;
- parseRail(element);
+ std::map<std::string, std::string> variables{};
+ parseRail(element, variables);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const std::invalid_argument& e)
@@ -719,7 +804,8 @@
"is_power_supply_rail": "true"
}
)"_json;
- parseRail(element);
+ std::map<std::string, std::string> variables{};
+ parseRail(element, variables);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const std::invalid_argument& e)
@@ -736,7 +822,8 @@
"check_status_vout": "false"
}
)"_json;
- parseRail(element);
+ std::map<std::string, std::string> variables{};
+ parseRail(element, variables);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const std::invalid_argument& e)
@@ -753,7 +840,8 @@
"compare_voltage_to_limit": 23
}
)"_json;
- parseRail(element);
+ std::map<std::string, std::string> variables{};
+ parseRail(element, variables);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const std::invalid_argument& e)
@@ -770,7 +858,8 @@
"gpio": 131
}
)"_json;
- parseRail(element);
+ std::map<std::string, std::string> variables{};
+ parseRail(element, variables);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const std::invalid_argument& e)
@@ -787,7 +876,8 @@
"check_status_vout": true
}
)"_json;
- parseRail(element);
+ std::map<std::string, std::string> variables{};
+ parseRail(element, variables);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const std::invalid_argument& e)
@@ -805,7 +895,8 @@
"compare_voltage_to_limit": true
}
)"_json;
- parseRail(element);
+ std::map<std::string, std::string> variables{};
+ parseRail(element, variables);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const std::invalid_argument& e)
@@ -822,13 +913,32 @@
"foo": "bar"
}
)"_json;
- parseRail(element);
+ std::map<std::string, std::string> variables{};
+ parseRail(element, variables);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const std::invalid_argument& e)
{
EXPECT_STREQ(e.what(), "Element contains an invalid property");
}
+
+ // Test where fails: Undefined variable specified
+ try
+ {
+ const json element = R"(
+ {
+ "name": "12.0VB",
+ "presence": "/xyz/openbmc_project/inventory/system/chassis/powersupply${chassis}"
+ }
+ )"_json;
+ std::map<std::string, std::string> variables{{"foo", "bar"}};
+ parseRail(element, variables);
+ ADD_FAILURE() << "Should not have reached this line.";
+ }
+ catch (const std::invalid_argument& e)
+ {
+ EXPECT_STREQ(e.what(), "Undefined variable: chassis");
+ }
}
TEST(ConfigFileParserTests, ParseRailArray)
@@ -839,7 +949,8 @@
[
]
)"_json;
- std::vector<std::unique_ptr<Rail>> rails = parseRailArray(element);
+ std::map<std::string, std::string> variables{};
+ auto rails = parseRailArray(element, variables);
EXPECT_EQ(rails.size(), 0);
}
@@ -851,12 +962,31 @@
{ "name": "VCS_CPU1" }
]
)"_json;
- std::vector<std::unique_ptr<Rail>> rails = parseRailArray(element);
+ std::map<std::string, std::string> variables{};
+ auto rails = parseRailArray(element, variables);
EXPECT_EQ(rails.size(), 2);
EXPECT_EQ(rails[0]->getName(), "VDD_CPU0");
EXPECT_EQ(rails[1]->getName(), "VCS_CPU1");
}
+ // Test where works: Variables specified
+ {
+ const json element = R"(
+ [
+ { "name": "${rail1}" },
+ { "name": "${rail2}" },
+ { "name": "${rail3}" }
+ ]
+ )"_json;
+ std::map<std::string, std::string> variables{
+ {"rail1", "foo"}, {"rail2", "bar"}, {"rail3", "baz"}};
+ auto rails = parseRailArray(element, variables);
+ EXPECT_EQ(rails.size(), 3);
+ EXPECT_EQ(rails[0]->getName(), "foo");
+ EXPECT_EQ(rails[1]->getName(), "bar");
+ EXPECT_EQ(rails[2]->getName(), "baz");
+ }
+
// Test where fails: Element is not an array
try
{
@@ -865,7 +995,8 @@
"foo": "bar"
}
)"_json;
- parseRailArray(element);
+ std::map<std::string, std::string> variables{};
+ parseRailArray(element, variables);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const std::invalid_argument& e)
@@ -882,13 +1013,33 @@
23
]
)"_json;
- parseRailArray(element);
+ std::map<std::string, std::string> variables{};
+ parseRailArray(element, variables);
ADD_FAILURE() << "Should not have reached this line.";
}
catch (const std::invalid_argument& e)
{
EXPECT_STREQ(e.what(), "Element is not an object");
}
+
+ // Test where fails: Invalid variable value specified
+ try
+ {
+ const json element = R"(
+ [
+ { "name": "VDD_CPU0", "page": "${page1}" },
+ { "name": "VCS_CPU1", "page": "${page2}" }
+ ]
+ )"_json;
+ std::map<std::string, std::string> variables{{"page1", "11"},
+ {"page2", "-1"}};
+ parseRailArray(element, variables);
+ ADD_FAILURE() << "Should not have reached this line.";
+ }
+ catch (const std::invalid_argument& e)
+ {
+ EXPECT_STREQ(e.what(), "Element is not an 8-bit unsigned integer");
+ }
}
TEST(ConfigFileParserTests, ParseRoot)
@@ -911,7 +1062,7 @@
]
}
)"_json;
- std::vector<std::unique_ptr<Rail>> rails = parseRoot(element);
+ auto rails = parseRoot(element);
EXPECT_EQ(rails.size(), 2);
EXPECT_EQ(rails[0]->getName(), "VDD_CPU0");
EXPECT_EQ(rails[1]->getName(), "VCS_CPU1");