pseq: Parsing support for chassis_template element
Add JSON parsing support for the new chassis_template element in the
phosphor-power-sequencer configuration file.
Also move the type alias 'json = nlohmann::json' from
config_file_parse.cpp to config_file_parser.hpp. This simplifies the
function declarations and allows them to textually match the function
definitions.
Tested:
* Ran automated tests.
Change-Id: I666ae0a6382ffec6643d3e268117d006b6c4dd2a
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 4539eb7..6d9b169 100644
--- a/phosphor-power-sequencer/src/config_file_parser.cpp
+++ b/phosphor-power-sequencer/src/config_file_parser.cpp
@@ -29,7 +29,6 @@
using namespace phosphor::power::json_parser_utils;
using ConfigFileParserError = phosphor::power::util::ConfigFileParserError;
-using json = nlohmann::json;
namespace fs = std::filesystem;
namespace phosphor::power::sequencer::config_file_parser
@@ -95,6 +94,44 @@
namespace internal
{
+std::tuple<std::string, JSONRefWrapper> parseChassisTemplate(
+ const json& element)
+{
+ verifyIsObject(element);
+ unsigned int propertyCount{0};
+
+ // Optional comments property; value not stored
+ if (element.contains("comments"))
+ {
+ ++propertyCount;
+ }
+
+ // Required id property
+ const json& idElement = getRequiredProperty(element, "id");
+ std::string id = parseString(idElement);
+ ++propertyCount;
+
+ // Required number property
+ // Just verify it exists; cannot be parsed without variable values
+ getRequiredProperty(element, "number");
+ ++propertyCount;
+
+ // Required inventory_path property
+ // Just verify it exists; cannot be parsed without variable values
+ getRequiredProperty(element, "inventory_path");
+ ++propertyCount;
+
+ // Required power_sequencers property
+ // Just verify it exists; cannot be parsed without variable values
+ getRequiredProperty(element, "power_sequencers");
+ ++propertyCount;
+
+ // Verify no invalid properties exist
+ verifyPropertyCount(element, propertyCount);
+
+ return {id, JSONRefWrapper{element}};
+}
+
GPIO parseGPIO(const json& element,
const std::map<std::string, std::string>& variables)
{
diff --git a/phosphor-power-sequencer/src/config_file_parser.hpp b/phosphor-power-sequencer/src/config_file_parser.hpp
index fcae99a..b2a18ca 100644
--- a/phosphor-power-sequencer/src/config_file_parser.hpp
+++ b/phosphor-power-sequencer/src/config_file_parser.hpp
@@ -23,12 +23,15 @@
#include <cstdint>
#include <filesystem>
+#include <functional> // for reference_wrapper
#include <map>
#include <memory>
#include <string>
#include <tuple>
#include <vector>
+using json = nlohmann::json;
+
namespace phosphor::power::sequencer::config_file_parser
{
@@ -80,6 +83,21 @@
namespace internal
{
+using JSONRefWrapper = std::reference_wrapper<const json>;
+
+/**
+ * Parses a JSON element containing a chassis_template object.
+ *
+ * Returns the template ID and a C++ reference_wrapper to the JSON element.
+ *
+ * Throws an exception if parsing fails.
+ *
+ * @param element JSON element
+ * @return template ID and reference_wrapper to JSON element
+ */
+std::tuple<std::string, JSONRefWrapper> parseChassisTemplate(
+ const json& element);
+
/**
* Parses a JSON element containing a GPIO.
*
@@ -91,7 +109,7 @@
* @param variables variables map used to expand variables in element value
* @return GPIO object
*/
-GPIO parseGPIO(const nlohmann::json& element,
+GPIO parseGPIO(const json& element,
const std::map<std::string, std::string>& variables);
/**
@@ -106,8 +124,7 @@
* @return tuple containing bus and address
*/
std::tuple<uint8_t, uint16_t> parseI2CInterface(
- const nlohmann::json& element,
- const std::map<std::string, std::string>& variables);
+ const json& element, const std::map<std::string, std::string>& variables);
/**
* Parses a JSON element containing a power_sequencer object.
@@ -118,12 +135,12 @@
*
* @param element JSON element
* @param variables variables map used to expand variables in element value
- * @param services System services like hardware presence and the journal
+ * @param services system services like hardware presence and the journal
* @return PowerSequencerDevice object
*/
std::unique_ptr<PowerSequencerDevice> parsePowerSequencer(
- const nlohmann::json& element,
- const std::map<std::string, std::string>& variables, Services& services);
+ const json& element, const std::map<std::string, std::string>& variables,
+ Services& services);
/**
* Parses a JSON element containing an array of power_sequencer objects.
@@ -134,12 +151,12 @@
*
* @param element JSON element
* @param variables variables map used to expand variables in element value
- * @param services System services like hardware presence and the journal
+ * @param services system services like hardware presence and the journal
* @return vector of PowerSequencerDevice objects
*/
std::vector<std::unique_ptr<PowerSequencerDevice>> parsePowerSequencerArray(
- const nlohmann::json& element,
- const std::map<std::string, std::string>& variables, Services& services);
+ const json& element, const std::map<std::string, std::string>& variables,
+ Services& services);
/**
* Parses a JSON element containing a rail.
@@ -153,8 +170,7 @@
* @return Rail object
*/
std::unique_ptr<Rail> parseRail(
- const nlohmann::json& element,
- const std::map<std::string, std::string>& variables);
+ const json& element, const std::map<std::string, std::string>& variables);
/**
* Parses a JSON element containing an array of rails.
@@ -168,8 +184,7 @@
* @return vector of Rail objects
*/
std::vector<std::unique_ptr<Rail>> parseRailArray(
- const nlohmann::json& element,
- const std::map<std::string, std::string>& variables);
+ const json& element, const std::map<std::string, std::string>& variables);
/**
* Parses the JSON root element of the entire configuration file.
@@ -181,7 +196,7 @@
* @param element JSON element
* @return vector of Rail objects
*/
-std::vector<std::unique_ptr<Rail>> parseRoot(const nlohmann::json& element);
+std::vector<std::unique_ptr<Rail>> parseRoot(const json& element);
} // namespace internal
diff --git a/phosphor-power-sequencer/test/config_file_parser_tests.cpp b/phosphor-power-sequencer/test/config_file_parser_tests.cpp
index c1e8426..a4a8040 100644
--- a/phosphor-power-sequencer/test/config_file_parser_tests.cpp
+++ b/phosphor-power-sequencer/test/config_file_parser_tests.cpp
@@ -326,6 +326,178 @@
}
}
+TEST(ConfigFileParserTests, ParseChassisTemplate)
+{
+ // Test where works: comments specified
+ {
+ const json element = R"(
+ {
+ "comments": [ "This is a template for the foo chassis type",
+ "Chassis contains a UCD90320 power sequencer" ],
+ "id": "foo_chassis",
+ "number": "${chassis_number}",
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}",
+ "power_sequencers": [
+ {
+ "type": "UCD90320",
+ "i2c_interface": { "bus": "${bus}", "address": "0x11" },
+ "power_control_gpio_name": "power-chassis${chassis_number}-control",
+ "power_good_gpio_name": "power-chassis${chassis_number}-good",
+ "rails": [ { "name": "VDD_CPU0" }, { "name": "VCS_CPU1" } ]
+ }
+ ]
+ }
+ )"_json;
+ auto [id, jsonRef] = parseChassisTemplate(element);
+ EXPECT_EQ(id, "foo_chassis");
+ EXPECT_EQ(jsonRef.get()["number"], "${chassis_number}");
+ EXPECT_EQ(jsonRef.get()["power_sequencers"].size(), 1);
+ EXPECT_EQ(jsonRef.get()["power_sequencers"][0]["type"], "UCD90320");
+ }
+
+ // Test where works: comments not specified
+ {
+ const json element = R"(
+ {
+ "id": "foo_chassis",
+ "number": "${chassis_number}",
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}",
+ "power_sequencers": []
+ }
+ )"_json;
+ auto [id, jsonRef] = parseChassisTemplate(element);
+ EXPECT_EQ(id, "foo_chassis");
+ EXPECT_EQ(jsonRef.get()["number"], "${chassis_number}");
+ EXPECT_EQ(
+ jsonRef.get()["inventory_path"],
+ "/xyz/openbmc_project/inventory/system/chassis${chassis_number}");
+ EXPECT_EQ(jsonRef.get()["power_sequencers"].size(), 0);
+ }
+
+ // Test where fails: Element is not an object
+ try
+ {
+ const json element = R"( [ "vdda", "vddb" ] )"_json;
+ parseChassisTemplate(element);
+ 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: Required id property not specified
+ try
+ {
+ const json element = R"(
+ {
+ "number": "${chassis_number}",
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}",
+ "power_sequencers": []
+ }
+ )"_json;
+ parseChassisTemplate(element);
+ ADD_FAILURE() << "Should not have reached this line.";
+ }
+ catch (const std::invalid_argument& e)
+ {
+ EXPECT_STREQ(e.what(), "Required property missing: id");
+ }
+
+ // Test where fails: Required number property not specified
+ try
+ {
+ const json element = R"(
+ {
+ "id": "foo_chassis",
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}",
+ "power_sequencers": []
+ }
+ )"_json;
+ parseChassisTemplate(element);
+ ADD_FAILURE() << "Should not have reached this line.";
+ }
+ catch (const std::invalid_argument& e)
+ {
+ EXPECT_STREQ(e.what(), "Required property missing: number");
+ }
+
+ // Test where fails: Required inventory_path property not specified
+ try
+ {
+ const json element = R"(
+ {
+ "id": "foo_chassis",
+ "number": "${chassis_number}",
+ "power_sequencers": []
+ }
+ )"_json;
+ parseChassisTemplate(element);
+ ADD_FAILURE() << "Should not have reached this line.";
+ }
+ catch (const std::invalid_argument& e)
+ {
+ EXPECT_STREQ(e.what(), "Required property missing: inventory_path");
+ }
+
+ // Test where fails: Required power_sequencers property not specified
+ try
+ {
+ const json element = R"(
+ {
+ "id": "foo_chassis",
+ "number": "${chassis_number}",
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}"
+ }
+ )"_json;
+ parseChassisTemplate(element);
+ ADD_FAILURE() << "Should not have reached this line.";
+ }
+ catch (const std::invalid_argument& e)
+ {
+ EXPECT_STREQ(e.what(), "Required property missing: power_sequencers");
+ }
+
+ // Test where fails: id value is invalid
+ try
+ {
+ const json element = R"(
+ {
+ "id": 13,
+ "number": "${chassis_number}",
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}",
+ "power_sequencers": []
+ }
+ )"_json;
+ parseChassisTemplate(element);
+ ADD_FAILURE() << "Should not have reached this line.";
+ }
+ catch (const std::invalid_argument& e)
+ {
+ EXPECT_STREQ(e.what(), "Element is not a string");
+ }
+
+ // Test where fails: Invalid property specified
+ try
+ {
+ const json element = R"(
+ {
+ "id": "foo_chassis",
+ "number": "${chassis_number}",
+ "inventory_path": "/xyz/openbmc_project/inventory/system/chassis${chassis_number}",
+ "power_sequencers": [],
+ "foo": "bar"
+ }
+ )"_json;
+ parseChassisTemplate(element);
+ ADD_FAILURE() << "Should not have reached this line.";
+ }
+ catch (const std::invalid_argument& e)
+ {
+ EXPECT_STREQ(e.what(), "Element contains an invalid property");
+ }
+}
+
TEST(ConfigFileParserTests, ParseGPIO)
{
// Test where works: Only required properties specified