blob: 44c7877e301f4ab446dcff9a214d203dce0cb721 [file] [log] [blame]
/**
* Copyright © 2024 IBM Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "config_file_parser.hpp"
#include "config_file_parser_error.hpp"
#include "json_parser_utils.hpp"
#include "ucd90160_device.hpp"
#include "ucd90320_device.hpp"
#include <cstdint>
#include <exception>
#include <fstream>
#include <optional>
#include <stdexcept>
using namespace phosphor::power::json_parser_utils;
using ConfigFileParserError = phosphor::power::util::ConfigFileParserError;
namespace fs = std::filesystem;
namespace phosphor::power::sequencer::config_file_parser
{
const std::filesystem::path standardConfigFileDirectory{
"/usr/share/phosphor-power-sequencer"};
std::filesystem::path find(
const std::vector<std::string>& compatibleSystemTypes,
const std::filesystem::path& configFileDir)
{
fs::path pathName, possiblePath;
std::string fileName;
for (const std::string& systemType : compatibleSystemTypes)
{
// Look for file name that is entire system type + ".json"
// Example: com.acme.Hardware.Chassis.Model.MegaServer.json
fileName = systemType + ".json";
possiblePath = configFileDir / fileName;
if (fs::is_regular_file(possiblePath))
{
pathName = possiblePath;
break;
}
// Look for file name that is last node of system type + ".json"
// Example: MegaServer.json
std::string::size_type pos = systemType.rfind('.');
if ((pos != std::string::npos) && ((systemType.size() - pos) > 1))
{
fileName = systemType.substr(pos + 1) + ".json";
possiblePath = configFileDir / fileName;
if (fs::is_regular_file(possiblePath))
{
pathName = possiblePath;
break;
}
}
}
return pathName;
}
std::vector<std::unique_ptr<Chassis>> parse(
const std::filesystem::path& pathName, Services& services)
{
try
{
// Use standard JSON parser to create tree of JSON elements
std::ifstream file{pathName};
json rootElement = json::parse(file);
// Parse tree of JSON elements and return corresponding C++ objects
return internal::parseRoot(rootElement, services);
}
catch (const std::exception& e)
{
throw ConfigFileParserError{pathName, e.what()};
}
}
namespace internal
{
std::unique_ptr<Chassis> parseChassis(
const json& element,
const std::map<std::string, JSONRefWrapper>& chassisTemplates,
Services& services)
{
verifyIsObject(element);
// If chassis object is not using a template, parse properties normally
if (!element.contains("template_id"))
{
bool isChassisTemplate{false};
return parseChassisProperties(element, isChassisTemplate, NO_VARIABLES,
services);
}
// Parse chassis object that is using a template
unsigned int propertyCount{0};
// Optional comments property; value not stored
if (element.contains("comments"))
{
++propertyCount;
}
// Required template_id property
const json& templateIDElement = getRequiredProperty(element, "template_id");
std::string templateID = parseString(templateIDElement);
++propertyCount;
// Required template_variable_values property
const json& variablesElement =
getRequiredProperty(element, "template_variable_values");
std::map<std::string, std::string> variables =
parseVariables(variablesElement);
++propertyCount;
// Verify no invalid properties exist
verifyPropertyCount(element, propertyCount);
// Get reference to chassis template JSON
auto it = chassisTemplates.find(templateID);
if (it == chassisTemplates.end())
{
throw std::invalid_argument{
"Invalid chassis template id: " + templateID};
}
const json& templateElement = it->second.get();
// Parse properties in template using variable values for this chassis
bool isChassisTemplate{true};
return parseChassisProperties(templateElement, isChassisTemplate, variables,
services);
}
std::vector<std::unique_ptr<Chassis>> parseChassisArray(
const json& element,
const std::map<std::string, JSONRefWrapper>& chassisTemplates,
Services& services)
{
verifyIsArray(element);
std::vector<std::unique_ptr<Chassis>> chassis;
for (auto& chassisElement : element)
{
chassis.emplace_back(
parseChassis(chassisElement, chassisTemplates, services));
}
return chassis;
}
std::unique_ptr<Chassis> parseChassisProperties(
const json& element, bool isChassisTemplate,
const std::map<std::string, std::string>& variables, Services& services)
{
verifyIsObject(element);
unsigned int propertyCount{0};
// Optional comments property; value not stored
if (element.contains("comments"))
{
++propertyCount;
}
// Required id property if this is a chassis template
// Don't parse again; this was already parsed by parseChassisTemplate()
if (isChassisTemplate)
{
getRequiredProperty(element, "id");
++propertyCount;
}
// Required number property
const json& numberElement = getRequiredProperty(element, "number");
unsigned int number = parseUnsignedInteger(numberElement, variables);
if (number < 1)
{
throw std::invalid_argument{"Invalid chassis number: Must be > 0"};
}
++propertyCount;
// Required inventory_path property
const json& inventoryPathElement =
getRequiredProperty(element, "inventory_path");
std::string inventoryPath =
parseString(inventoryPathElement, false, variables);
++propertyCount;
// Required power_sequencers property
const json& powerSequencersElement =
getRequiredProperty(element, "power_sequencers");
std::vector<std::unique_ptr<PowerSequencerDevice>> powerSequencers =
parsePowerSequencerArray(powerSequencersElement, variables, services);
++propertyCount;
// Verify no invalid properties exist
verifyPropertyCount(element, propertyCount);
return std::make_unique<Chassis>(number, inventoryPath,
std::move(powerSequencers));
}
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}};
}
std::map<std::string, JSONRefWrapper> parseChassisTemplateArray(
const json& element)
{
verifyIsArray(element);
std::map<std::string, JSONRefWrapper> chassisTemplates;
for (auto& chassisTemplateElement : element)
{
chassisTemplates.emplace(parseChassisTemplate(chassisTemplateElement));
}
return chassisTemplates;
}
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, variables);
++propertyCount;
// Optional active_low property
bool activeLow{false};
auto activeLowIt = element.find("active_low");
if (activeLowIt != element.end())
{
activeLow = parseBoolean(*activeLowIt, variables);
++propertyCount;
}
// Verify no invalid properties exist
verifyPropertyCount(element, propertyCount);
return GPIO(line, activeLow);
}
std::tuple<uint8_t, uint16_t> parseI2CInterface(
const nlohmann::json& element,
const std::map<std::string, std::string>& variables)
{
verifyIsObject(element);
unsigned int propertyCount{0};
// Required bus property
const json& busElement = getRequiredProperty(element, "bus");
uint8_t bus = parseUint8(busElement, variables);
++propertyCount;
// Required address property
const json& addressElement = getRequiredProperty(element, "address");
uint16_t address = parseHexByte(addressElement, variables);
++propertyCount;
// Verify no invalid properties exist
verifyPropertyCount(element, propertyCount);
return {bus, address};
}
std::unique_ptr<PowerSequencerDevice> parsePowerSequencer(
const nlohmann::json& element,
const std::map<std::string, std::string>& variables, Services& services)
{
verifyIsObject(element);
unsigned int propertyCount{0};
// Optional comments property; value not stored
if (element.contains("comments"))
{
++propertyCount;
}
// Required type property
const json& typeElement = getRequiredProperty(element, "type");
std::string type = parseString(typeElement, false, variables);
++propertyCount;
// Required i2c_interface property
const json& i2cInterfaceElement =
getRequiredProperty(element, "i2c_interface");
auto [bus, address] = parseI2CInterface(i2cInterfaceElement, variables);
++propertyCount;
// Required power_control_gpio_name property
const json& powerControlGPIONameElement =
getRequiredProperty(element, "power_control_gpio_name");
std::string powerControlGPIOName =
parseString(powerControlGPIONameElement, false, variables);
++propertyCount;
// Required power_good_gpio_name property
const json& powerGoodGPIONameElement =
getRequiredProperty(element, "power_good_gpio_name");
std::string powerGoodGPIOName =
parseString(powerGoodGPIONameElement, false, variables);
++propertyCount;
// Required rails property
const json& railsElement = getRequiredProperty(element, "rails");
std::vector<std::unique_ptr<Rail>> rails =
parseRailArray(railsElement, variables);
++propertyCount;
// Verify no invalid properties exist
verifyPropertyCount(element, propertyCount);
if (type == UCD90160Device::deviceName)
{
return std::make_unique<UCD90160Device>(
bus, address, powerControlGPIOName, powerGoodGPIOName,
std::move(rails), services);
}
else if (type == UCD90320Device::deviceName)
{
return std::make_unique<UCD90320Device>(
bus, address, powerControlGPIOName, powerGoodGPIOName,
std::move(rails), services);
}
throw std::invalid_argument{"Invalid power sequencer type: " + type};
}
std::vector<std::unique_ptr<PowerSequencerDevice>> parsePowerSequencerArray(
const nlohmann::json& element,
const std::map<std::string, std::string>& variables, Services& services)
{
verifyIsArray(element);
std::vector<std::unique_ptr<PowerSequencerDevice>> powerSequencers;
for (auto& powerSequencerElement : element)
{
powerSequencers.emplace_back(
parsePowerSequencer(powerSequencerElement, variables, services));
}
return powerSequencers;
}
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, false, variables);
++propertyCount;
// Optional presence property
std::optional<std::string> presence{};
auto presenceIt = element.find("presence");
if (presenceIt != element.end())
{
presence = parseString(*presenceIt, false, variables);
++propertyCount;
}
// Optional page property
std::optional<uint8_t> page{};
auto pageIt = element.find("page");
if (pageIt != element.end())
{
page = parseUint8(*pageIt, variables);
++propertyCount;
}
// Optional is_power_supply_rail property
bool isPowerSupplyRail{false};
auto isPowerSupplyRailIt = element.find("is_power_supply_rail");
if (isPowerSupplyRailIt != element.end())
{
isPowerSupplyRail = parseBoolean(*isPowerSupplyRailIt, variables);
++propertyCount;
}
// Optional check_status_vout property
bool checkStatusVout{false};
auto checkStatusVoutIt = element.find("check_status_vout");
if (checkStatusVoutIt != element.end())
{
checkStatusVout = parseBoolean(*checkStatusVoutIt, variables);
++propertyCount;
}
// Optional compare_voltage_to_limit property
bool compareVoltageToLimit{false};
auto compareVoltageToLimitIt = element.find("compare_voltage_to_limit");
if (compareVoltageToLimitIt != element.end())
{
compareVoltageToLimit =
parseBoolean(*compareVoltageToLimitIt, variables);
++propertyCount;
}
// Optional gpio property
std::optional<GPIO> gpio{};
auto gpioIt = element.find("gpio");
if (gpioIt != element.end())
{
gpio = parseGPIO(*gpioIt, variables);
++propertyCount;
}
// If check_status_vout or compare_voltage_to_limit property is true, the
// page property is required; verify page was specified
if ((checkStatusVout || compareVoltageToLimit) && !page.has_value())
{
throw std::invalid_argument{"Required property missing: page"};
}
// Verify no invalid properties exist
verifyPropertyCount(element, propertyCount);
return std::make_unique<Rail>(name, presence, page, isPowerSupplyRail,
checkStatusVout, compareVoltageToLimit, gpio);
}
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, variables));
}
return rails;
}
std::vector<std::unique_ptr<Chassis>> parseRoot(const json& element,
Services& services)
{
verifyIsObject(element);
unsigned int propertyCount{0};
// 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 chassis;
}
std::map<std::string, std::string> parseVariables(const json& element)
{
verifyIsObject(element);
std::map<std::string, std::string> variables;
std::string name, value;
for (const auto& [nameElement, valueElement] : element.items())
{
name = parseString(nameElement);
value = parseString(valueElement);
variables.emplace(name, value);
}
return variables;
}
} // namespace internal
} // namespace phosphor::power::sequencer::config_file_parser