control: add command line tool to retrieve fan status (get function)
This is part two of a multipart commit to create a fan-control command
line utility to report service status, show target/actual RPM/PWM info,
and manually control single fans. This commit implements the "get"
command. Further functionality will come in subsequent commits.
Sample output: /tmp/fanctl get
TARGET SENSOR TARGET(RPM) FEEDBACK SENSOR FEEDBACK(RPMS)
===============================================================
fan0_0 10000 fan0_0 10000
fan0_1 13591
fan1_0 10000 fan1_0 10000
fan1_1 13591
fan2_0 10000 fan2_0 10000
fan2_1 13591
fan3_0 10000 fan3_0 10000
fan3_1 13591
fan4_0 10000 fan4_0 10000
fan4_1 13591
fan5_0 10000 fan5_0 10000
fan5_1 13591
Signed-off-by: Mike Capps <mikepcapps@gmail.com>
Signed-off-by: Matthew Barth <msbarth@us.ibm.com>
Change-Id: I5a918b34b807fa284c0072db2141aa572ae0f0da
diff --git a/control/fanctl.cpp b/control/fanctl.cpp
index f5e77f2..07ae84b 100644
--- a/control/fanctl.cpp
+++ b/control/fanctl.cpp
@@ -24,26 +24,6 @@
using SDBusPlus = phosphor::fan::util::SDBusPlus;
-constexpr auto phosphorServiceName = "phosphor-fan-control@0.service";
-constexpr auto systemdMgrIface = "org.freedesktop.systemd1.Manager";
-constexpr auto systemdPath = "/org/freedesktop/systemd1";
-constexpr auto systemdService = "org.freedesktop.systemd1";
-
-std::map<std::string, std::string> interfaces{
- {"FanSpeed", "xyz.openbmc_project.Control.FanSpeed"},
- {"FanPwm", "xyz.openbmc_project.Control.FanPwm"},
- {"SensorValue", "xyz.openbmc_project.Sensor.Value"},
- {"Item", "xyz.openbmc_project.Inventory.Item"},
- {"OpStatus", "xyz.openbmc_project.State.Decorator.OperationalStatus"}};
-
-std::map<std::string, std::string> paths{
- {"motherboard",
- "/xyz/openbmc_project/inventory/system/chassis/motherboard"},
- {"tach", "/xyz/openbmc_project/sensors/fan_tach"}};
-
-// paths by D-bus interface,fan name
-std::map<std::string, std::map<std::string, std::vector<std::string>>> pathMap;
-
/**
* @function extracts fan name from dbus path string (last token where
* delimiter is the / character), with proper bounds checking.
@@ -103,6 +83,73 @@
}
/**
+ * @function consolidated function to load dbus paths and fan names
+ */
+auto loadDBusData()
+{
+ auto& bus{SDBusPlus::getBus()};
+
+ std::vector<std::string> fanNames;
+
+ // paths by D-bus interface,fan name
+ std::map<std::string, std::map<std::string, std::vector<std::string>>>
+ pathMap;
+
+ std::string method("RPM");
+
+ std::map<const std::string, const std::string> interfaces{
+ {"FanSpeed", "xyz.openbmc_project.Control.FanSpeed"},
+ {"FanPwm", "xyz.openbmc_project.Control.FanPwm"},
+ {"SensorValue", "xyz.openbmc_project.Sensor.Value"},
+ {"Item", "xyz.openbmc_project.Inventory.Item"},
+ {"OpStatus", "xyz.openbmc_project.State.Decorator.OperationalStatus"}};
+
+ std::map<const std::string, const std::string> paths{
+ {"motherboard",
+ "/xyz/openbmc_project/inventory/system/chassis/motherboard"},
+ {"tach", "/xyz/openbmc_project/sensors/fan_tach"}};
+
+ // build a list of all fans
+ for (auto& path : SDBusPlus::getSubTreePathsRaw(bus, paths["tach"],
+ interfaces["FanSpeed"], 0))
+ {
+ // special case where we build the list of fans
+ auto fan = justFanName(path);
+ fan = fan.substr(0, fan.rfind("_"));
+ fanNames.push_back(fan);
+ }
+
+ // retry with PWM mode if none found
+ if (0 == fanNames.size())
+ {
+ method = "PWM";
+
+ for (auto& path : SDBusPlus::getSubTreePathsRaw(
+ bus, paths["tach"], interfaces["FanPwm"], 0))
+ {
+ // special case where we build the list of fans
+ auto fan = justFanName(path);
+ fan = fan.substr(0, fan.rfind("_"));
+ fanNames.push_back(fan);
+ }
+ }
+
+ // load tach sensor paths for each fan
+ pathMap["tach"] =
+ getPathsFromIface(paths["tach"], interfaces["SensorValue"], fanNames);
+
+ // load inventory Item data for each fan
+ pathMap["inventory"] = getPathsFromIface(
+ paths["motherboard"], interfaces["Item"], fanNames, true);
+
+ // load operational status data for each fan
+ pathMap["opstatus"] = getPathsFromIface(
+ paths["motherboard"], interfaces["OpStatus"], fanNames, true);
+
+ return std::make_tuple(fanNames, pathMap, interfaces, method);
+}
+
+/**
* @function gets the states of phosphor-fanctl. equivalent to
* "systemctl status phosphor-fan-control@0"
* @return a list of several (sub)states of fanctl (loaded,
@@ -117,9 +164,13 @@
std::string, std::string, sdbusplus::message::object_path,
uint32_t, std::string, sdbusplus::message::object_path>;
+ constexpr auto systemdMgrIface = "org.freedesktop.systemd1.Manager";
+ constexpr auto systemdPath = "/org/freedesktop/systemd1";
+ constexpr auto systemdService = "org.freedesktop.systemd1";
+
std::array<std::string, 6> ret;
- std::vector<std::string> services{phosphorServiceName};
+ std::vector<std::string> services{"phosphor-fan-control@0.service"};
try
{
@@ -139,7 +190,7 @@
<< std::endl;
}
}
- catch (std::exception& e)
+ catch (const std::exception& e)
{
std::cerr << "Failure retrieving phosphor-fan-control states: "
<< e.what() << std::endl;
@@ -164,6 +215,14 @@
}
/**
+ * @function helper to determine interface type from a given control method
+ */
+std::string ifaceTypeFromMethod(const std::string& method)
+{
+ return (method == "RPM" ? "FanSpeed" : "FanPwm");
+}
+
+/**
* @function performs the "status" command from the cmdline.
* get states and sensor data and output to the console
*/
@@ -173,51 +232,10 @@
using std::endl;
using std::setw;
- auto& bus{SDBusPlus::getBus()};
+ auto busData = loadDBusData();
+ auto& method = std::get<3>(busData);
- std::string tmethod("RPM"), fmethod("RPMS"), property;
-
- std::vector<std::string> sensorPaths, fanNames;
-
- // build a list of all fans
- for (auto& path : SDBusPlus::getSubTreePathsRaw(bus, paths["tach"],
- interfaces["FanSpeed"], 0))
- {
- // special case where we build the list of fans
- auto fan = justFanName(path);
- fan = fan.substr(0, fan.rfind("_"));
- fanNames.push_back(fan);
- }
-
- // retry if none found
- if (0 == fanNames.size())
- {
- tmethod = "PWM";
- fmethod = "PWM";
-
- for (auto& path : SDBusPlus::getSubTreePathsRaw(
- bus, paths["tach"], interfaces["FanPwm"], 0))
- {
- auto fan = justFanName(path);
- fan = fan.substr(0, fan.rfind("_"));
- fanNames.push_back(fan);
- }
- }
-
- // load tach sensor paths for each fan
- pathMap["tach"] =
- getPathsFromIface(paths["tach"], interfaces["SensorValue"], fanNames);
-
- // load speed sensor paths for each fan
- pathMap["speed"] = pathMap["tach"];
-
- // load inventory Item data for each fan
- pathMap["inventory"] = getPathsFromIface(
- paths["motherboard"], interfaces["Item"], fanNames, true);
-
- // load operational status data for each fan
- pathMap["opstatus"] = getPathsFromIface(
- paths["motherboard"], interfaces["OpStatus"], fanNames, true);
+ std::string property;
// get the state,substate of fan-control and obmc
auto states = getStates();
@@ -231,11 +249,15 @@
cout << "CurrentHostState : " << states[5] << endl;
cout << endl;
cout << " FAN "
- << "TARGET(" << tmethod << ") FEEDBACKS(RPMS) PRESENT"
+ << "TARGET(" << method << ") FEEDBACKS(RPMS) PRESENT"
<< " FUNCTIONAL" << endl;
cout << "==============================================================="
<< endl;
+ auto& fanNames{std::get<0>(busData)};
+ auto& pathMap{std::get<1>(busData)};
+ auto& interfaces{std::get<2>(busData)};
+
for (auto& fan : fanNames)
{
cout << " " << fan << setw(18);
@@ -243,11 +265,13 @@
// get the target RPM
property = "Target";
cout << SDBusPlus::getProperty<uint64_t>(
- pathMap["speed"][fan][0], interfaces["FanSpeed"], property)
+ pathMap["tach"][fan][0],
+ interfaces[ifaceTypeFromMethod(method)], property)
<< setw(11);
// get the sensor RPM
property = "Value";
+
int numRotors = pathMap["tach"][fan].size();
// print tach readings for each rotor
for (auto& path : pathMap["tach"][fan])
@@ -261,7 +285,7 @@
}
cout << setw(10);
- // get the present property
+ // print the Present property
property = "Present";
std::string val;
for (auto& path : pathMap["inventory"][fan])
@@ -287,7 +311,7 @@
cout << setw(13);
- // get the functional property
+ // and the functional property
property = "Functional";
for (auto& path : pathMap["opstatus"][fan])
{
@@ -315,6 +339,65 @@
}
/**
+ * @function print target RPM/PWM and tach readings from each fan
+ */
+void get()
+{
+ using std::cout;
+ using std::endl;
+ using std::setw;
+
+ auto busData = loadDBusData();
+
+ auto& fanNames{std::get<0>(busData)};
+ auto& pathMap{std::get<1>(busData)};
+ auto& interfaces{std::get<2>(busData)};
+ auto& method = std::get<3>(busData);
+
+ std::string property;
+
+ // print the header
+ cout << "TARGET SENSOR" << setw(11) << "TARGET(" << method
+ << ") FEEDBACK SENSOR ";
+ cout << "FEEDBACK(" << method << ")" << endl;
+ cout << "==============================================================="
+ << endl;
+
+ for (auto& fan : fanNames)
+ {
+ if (pathMap["tach"][fan].size() == 0)
+ continue;
+ // print just the sensor name
+ auto shortPath = pathMap["tach"][fan][0];
+ shortPath = justFanName(shortPath);
+ cout << shortPath << setw(22);
+
+ // print its target RPM/PWM
+ property = "Target";
+ cout << SDBusPlus::getProperty<uint64_t>(
+ pathMap["tach"][fan][0],
+ interfaces[ifaceTypeFromMethod(method)], property)
+ << setw(12) << " ";
+
+ // print readings for each rotor
+ property = "Value";
+
+ auto indent = 0U;
+ for (auto& path : pathMap["tach"][fan])
+ {
+ cout << setw(indent);
+ cout << justFanName(path) << setw(17)
+ << SDBusPlus::getProperty<double>(
+ path, interfaces["SensorValue"], property)
+ << endl;
+
+ if (0 == indent)
+ indent = 46;
+ }
+ }
+}
+
+/**
* @function main entry point for the application
*/
int main(int argc, char* argv[])
@@ -344,12 +427,25 @@
"states, and fan-monitor/BMC/Power service status");
cmdStatus->require_option(0);
+ // Get method
+ auto cmdGet = commands->add_subcommand(
+ "get",
+ "Get the current fan target and feedback speeds for all rotors");
+ cmdGet->set_help_flag(
+ "-h, --help",
+ "Get the current fan target and feedback speeds for all rotors");
+ cmdGet->require_option(0);
+
CLI11_PARSE(app, argc, argv);
if (app.got_subcommand("status"))
{
status();
}
+ else if (app.got_subcommand("get"))
+ {
+ get();
+ }
}
catch (const std::exception& e)
{