psutils: Add --get-model option
Add a --get-model command line option to the psutils tool. This option
obtains the PSU model using information in sysfs.
Supports both methods of obtaining information about a PSU:
* psu.json file
* D-Bus
Tested:
* Verified --get-version still works
* With psu.json file
* Without psu.json file
* Verified new --get-model property works
* With psu.json file
* Without psu.json file
* Tested all error paths
* See the following gist for the complete test plan:
https://gist.github.com/smccarney/859ffaaa94ce12992af1b24e6c899962
Change-Id: If1be01f4b70ad9d80ce01402c57730990fd2c2ea
Signed-off-by: Shawn McCarney <shawnmm@us.ibm.com>
diff --git a/tools/power-utils/main.cpp b/tools/power-utils/main.cpp
index 8fae3a1..8ca7c04 100644
--- a/tools/power-utils/main.cpp
+++ b/tools/power-utils/main.cpp
@@ -15,6 +15,7 @@
*/
#include "config.h"
+#include "model.hpp"
#include "updater.hpp"
#include "utils.hpp"
#include "version.hpp"
@@ -30,15 +31,17 @@
int main(int argc, char** argv)
{
- std::string psuPath;
+ std::string psuPathVersion, psuPathModel;
std::vector<std::string> versions;
bool rawOutput = false;
std::vector<std::string> updateArguments;
CLI::App app{"PSU utils app for OpenBMC"};
auto action = app.add_option_group("Action");
- action->add_option("-g,--get-version", psuPath,
+ action->add_option("-g,--get-version", psuPathVersion,
"Get PSU version from inventory path");
+ action->add_option("-m,--get-model", psuPathModel,
+ "Get PSU model from inventory path");
action->add_option("-c,--compare", versions,
"Compare and get the latest version");
action
@@ -54,17 +57,21 @@
bool useJsonFile = utils::checkFileExists(PSU_JSON_PATH);
auto bus = sdbusplus::bus::new_default();
- if (!psuPath.empty())
+ if (!psuPathVersion.empty())
{
if (!useJsonFile)
{
- ret = version::getVersion(bus, psuPath);
+ ret = version::getVersion(bus, psuPathVersion);
}
else
{
- ret = version::getVersion(psuPath);
+ ret = version::getVersion(psuPathVersion);
}
}
+ if (!psuPathModel.empty())
+ {
+ ret = model::getModel(bus, psuPathModel);
+ }
if (!versions.empty())
{
ret = version::getLatest(versions);
diff --git a/tools/power-utils/meson.build b/tools/power-utils/meson.build
index 41149da..542eadb 100644
--- a/tools/power-utils/meson.build
+++ b/tools/power-utils/meson.build
@@ -3,6 +3,7 @@
'version.cpp',
'updater.cpp',
'utils.cpp',
+ 'model.cpp',
'main.cpp',
dependencies: [
cli11_dep,
diff --git a/tools/power-utils/model.cpp b/tools/power-utils/model.cpp
new file mode 100644
index 0000000..6677eac
--- /dev/null
+++ b/tools/power-utils/model.cpp
@@ -0,0 +1,162 @@
+/**
+ * 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.h"
+
+#include "model.hpp"
+
+#include "pmbus.hpp"
+#include "utility.hpp"
+#include "utils.hpp"
+
+#include <nlohmann/json.hpp>
+#include <phosphor-logging/log.hpp>
+
+#include <exception>
+#include <format>
+#include <fstream>
+#include <stdexcept>
+
+using json = nlohmann::json;
+
+using namespace utils;
+using namespace phosphor::logging;
+using namespace phosphor::power::util;
+
+namespace model
+{
+
+namespace internal
+{
+
+/**
+ * @brief Get the PSU model from sysfs.
+ *
+ * Obtain PSU information from the PSU JSON file.
+ *
+ * Throws an exception if an error occurs.
+ *
+ * @param[in] psuInventoryPath - PSU D-Bus inventory path
+ *
+ * @return PSU model
+ */
+std::string getModelJson(const std::string& psuInventoryPath)
+{
+ // Parse PSU JSON file
+ std::ifstream file{PSU_JSON_PATH};
+ json data = json::parse(file);
+
+ // Get PSU device path from JSON
+ auto it = data.find("psuDevices");
+ if (it == data.end())
+ {
+ throw std::runtime_error{"Unable to find psuDevices"};
+ }
+ auto device = it->find(psuInventoryPath);
+ if (device == it->end())
+ {
+ throw std::runtime_error{std::format(
+ "Unable to find device path for PSU {}", psuInventoryPath)};
+ }
+ std::string devicePath = *device;
+ if (devicePath.empty())
+ {
+ throw std::runtime_error{
+ std::format("Empty device path for PSU {}", psuInventoryPath)};
+ }
+
+ // Get sysfs filename from JSON for Model information
+ it = data.find("fruConfigs");
+ if (it == data.end())
+ {
+ throw std::runtime_error{"Unable to find fruConfigs"};
+ }
+ std::string fileName;
+ for (const auto& fru : *it)
+ {
+ if (fru.contains("propertyName") && (fru["propertyName"] == "Model") &&
+ fru.contains("fileName"))
+ {
+ fileName = fru["fileName"];
+ break;
+ }
+ }
+ if (fileName.empty())
+ {
+ throw std::runtime_error{"Unable to find file name for Model"};
+ }
+
+ // Get PMBus access type from JSON
+ phosphor::pmbus::Type type = getPMBusAccessType(data);
+
+ // Read model from sysfs file
+ phosphor::pmbus::PMBus pmbus(devicePath);
+ std::string model = pmbus.readString(fileName, type);
+ return model;
+}
+
+/**
+ * @brief Get the PSU model from sysfs.
+ *
+ * Obtain PSU information from D-Bus.
+ *
+ * Throws an exception if an error occurs.
+ *
+ * @param[in] bus - D-Bus connection
+ * @param[in] psuInventoryPath - PSU D-Bus inventory path
+ *
+ * @return PSU model
+ */
+std::string getModelDbus(sdbusplus::bus_t& bus,
+ const std::string& psuInventoryPath)
+{
+ // Get PSU I2C bus/address and create PMBus interface
+ const auto [i2cBus, i2cAddr] = getPsuI2c(bus, psuInventoryPath);
+ auto pmbus = getPmbusIntf(i2cBus, i2cAddr);
+
+ // Read model from sysfs file
+ std::string fileName = "ccin";
+ auto type = phosphor::pmbus::Type::HwmonDeviceDebug;
+ std::string model = pmbus->readString(fileName, type);
+ return model;
+}
+
+} // namespace internal
+
+std::string getModel(sdbusplus::bus_t& bus, const std::string& psuInventoryPath)
+{
+ std::string model;
+ try
+ {
+ // If PSU JSON file exists
+ if (checkFileExists(PSU_JSON_PATH))
+ {
+ // Obtain PSU information from JSON file
+ model = internal::getModelJson(psuInventoryPath);
+ }
+ else
+ {
+ // Obtain PSU information from D-Bus
+ model = internal::getModelDbus(bus, psuInventoryPath);
+ }
+ }
+ catch (const std::exception& e)
+ {
+ log<level::ERR>(std::format("Error: {}", e.what()).c_str());
+ }
+ return model;
+}
+
+} // namespace model
diff --git a/tools/power-utils/model.hpp b/tools/power-utils/model.hpp
new file mode 100644
index 0000000..413e28e
--- /dev/null
+++ b/tools/power-utils/model.hpp
@@ -0,0 +1,36 @@
+/**
+ * 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.
+ */
+#pragma once
+
+#include <sdbusplus/bus.hpp>
+
+#include <string>
+
+namespace model
+{
+
+/**
+ * @brief Get the PSU model from sysfs.
+ *
+ * @param[in] bus - D-Bus connection
+ * @param[in] psuInventoryPath - PSU D-Bus inventory path
+ *
+ * @return PSU model, or "" if model could not be found
+ */
+std::string getModel(sdbusplus::bus_t& bus,
+ const std::string& psuInventoryPath);
+
+} // namespace model