blob: 95ceb3d6849ca90221e624745e7803cfc4b4d78f [file] [log] [blame]
Tomáš Pecka339bc672020-11-11 15:59:03 +01001/*
2 * Copyright (C) 2016-2020 CESNET, https://photonics.cesnet.cz/
3 *
4 * Written by Tomáš Pecka <tomas.pecka@fit.cvut.cz>
5 *
6 */
7
Václav Kubernát069d3a92021-11-14 12:37:46 +01008#include <chrono>
Tomáš Pecka339bc672020-11-11 15:59:03 +01009#include <utility>
10#include "IETFHardware.h"
11#include "utils/log.h"
12#include "utils/time.h"
13
14using namespace std::literals;
15
16namespace {
17
Tomáš Pecka83b62e12020-12-16 14:50:49 +010018static const std::string ietfHardwareStatePrefix = "/ietf-hardware:hardware";
Tomáš Pecka339bc672020-11-11 15:59:03 +010019
20/** @brief Constructs a full XPath for a specific component */
21std::string xpathForComponent(const std::string& componentName)
22{
23 return ietfHardwareStatePrefix + "/component[name='" + componentName + "']/";
24}
25
26/** @brief Prefix all properties from values DataTree with a component name (calculated from @p componentName) and push them into the DataTree */
27void addComponent(velia::ietf_hardware::DataTree& res, const std::string& componentName, const std::optional<std::string>& parent, const velia::ietf_hardware::DataTree& values)
28{
29 auto componentPrefix = xpathForComponent(componentName);
30
31 if (parent) {
32 res[componentPrefix + "parent"] = *parent;
33 }
34 for (const auto& [k, v] : values) {
35 res[componentPrefix + k] = v;
36 }
Tomáš Pecka7eb0c422023-04-21 15:36:33 +020037
38 res[componentPrefix + "state/oper-state"] = "enabled";
Tomáš Pecka339bc672020-11-11 15:59:03 +010039}
40
41/** @brief Write a sensor-data @p value for a component @p componentName and push it into the @p res DataTree */
42void addSensorValue(velia::ietf_hardware::DataTree& res, const std::string& componentName, const std::string& value)
43{
44 const auto componentPrefix = xpathForComponent(componentName);
45 res[componentPrefix + "sensor-data/value"] = value;
46}
47}
48
49namespace velia::ietf_hardware {
50
51IETFHardware::IETFHardware() = default;
52
53IETFHardware::~IETFHardware() = default;
54
55std::map<std::string, std::string> IETFHardware::process()
56{
57 std::map<std::string, std::string> res;
58
59 for (auto& dataReader : m_callbacks) {
60 res.merge(dataReader());
61 }
62
63 res[ietfHardwareStatePrefix + "/last-change"] = velia::utils::yangTimeFormat(std::chrono::system_clock::now());
64 return res;
65}
66
Tomáš Pecka339bc672020-11-11 15:59:03 +010067/** @brief A namespace containing predefined data readers for IETFHardware class.
68 * @see IETFHardware for more information
69 */
70namespace data_reader {
71
72DataReader::DataReader(std::string componentName, std::optional<std::string> parent)
73 : m_componentName(std::move(componentName))
74 , m_parent(std::move(parent))
75{
76}
77
78/** @brief Constructs a component without any sensor-data and provide only the static data @p dataTree passed via constructor.
79 * @param componentName the name of the component in the resulting tree
80 * @param parent The component in YANG model has a link to parent. Specify who is the parent.
81 * @param dataTree static data to insert into the resulting tree. The dataTree keys should only contain the YANG node name, not full XPath. The full XPath is constructed from @componentName and the map key.
82 */
83StaticData::StaticData(std::string componentName, std::optional<std::string> parent, DataTree dataTree)
84 : DataReader(std::move(componentName), std::move(parent))
85{
86 addComponent(m_staticData,
87 m_componentName,
88 m_parent,
89 dataTree);
90}
91
92DataTree StaticData::operator()() const { return m_staticData; }
Tomáš Pecka4886db22023-05-10 10:46:15 +020093ThresholdsBySensorPath StaticData::thresholds() const { return {}; }
Tomáš Pecka339bc672020-11-11 15:59:03 +010094
Tomáš Pecka4886db22023-05-10 10:46:15 +020095Fans::Fans(std::string componentName, std::optional<std::string> parent, std::shared_ptr<sysfs::HWMon> hwmon, unsigned fanChannelsCount, Thresholds<int64_t> thresholds)
Tomáš Pecka339bc672020-11-11 15:59:03 +010096 : DataReader(std::move(componentName), std::move(parent))
97 , m_hwmon(std::move(hwmon))
98 , m_fanChannelsCount(fanChannelsCount)
Tomáš Pecka4886db22023-05-10 10:46:15 +020099 , m_thresholds(std::move(thresholds))
Tomáš Pecka339bc672020-11-11 15:59:03 +0100100{
101 // fans
102 addComponent(m_staticData,
103 m_componentName,
104 m_parent,
Tomáš Pecka77e09c22023-05-04 20:39:57 +0200105 DataTree{
Tomáš Pecka339bc672020-11-11 15:59:03 +0100106 {"class", "iana-hardware:module"}, // FIXME: Read (or pass via constructor) additional properties (mfg, model, ...). They should be in the fans' tray EEPROM.
107 });
108
109 for (unsigned i = 1; i <= m_fanChannelsCount; i++) {
110 // fans -> fan_i
111 addComponent(m_staticData,
112 m_componentName + ":fan" + std::to_string(i),
113 m_componentName,
Tomáš Pecka77e09c22023-05-04 20:39:57 +0200114 DataTree{
Tomáš Pecka339bc672020-11-11 15:59:03 +0100115 {"class", "iana-hardware:fan"},
116 });
117
118 // fans -> fan_i -> sensor-data
119 addComponent(m_staticData,
120 m_componentName + ":fan" + std::to_string(i) + ":rpm",
121 m_componentName + ":fan" + std::to_string(i),
Tomáš Pecka77e09c22023-05-04 20:39:57 +0200122 DataTree{
Tomáš Pecka339bc672020-11-11 15:59:03 +0100123 {"class", "iana-hardware:sensor"},
124 {"sensor-data/value-type", "rpm"},
125 {"sensor-data/value-scale", "units"},
126 {"sensor-data/value-precision", "0"},
127 {"sensor-data/oper-status", "ok"},
128 });
129 }
130}
131
132DataTree Fans::operator()() const
133{
134 DataTree res(m_staticData);
135
Tomáš Pecka339bc672020-11-11 15:59:03 +0100136 for (unsigned i = 1; i <= m_fanChannelsCount; i++) {
137 const auto sensorComponentName = m_componentName + ":fan" + std::to_string(i) + ":rpm";
138 const auto attribute = "fan"s + std::to_string(i) + "_input";
139
Václav Kubernátb0939dd2021-04-28 04:08:48 +0200140 addSensorValue(res, sensorComponentName, std::to_string(m_hwmon->attribute(attribute)));
Tomáš Pecka339bc672020-11-11 15:59:03 +0100141 }
142
143 return res;
144}
145
Tomáš Pecka4886db22023-05-10 10:46:15 +0200146ThresholdsBySensorPath Fans::thresholds() const
147{
148 ThresholdsBySensorPath res;
149
150 for (unsigned i = 1; i <= m_fanChannelsCount; i++) {
151 res.emplace(xpathForComponent(m_componentName + ":fan" + std::to_string(i) + ":rpm") + "sensor-data/value", m_thresholds);
152 }
153
154 return res;
155}
156
Václav Kubernát6c17d0a2021-03-29 04:55:31 +0200157std::string getSysfsFilename(const SensorType type, int sysfsChannelNr)
158{
159 switch (type) {
Tomáš Pecka77e09c22023-05-04 20:39:57 +0200160 case SensorType::Temperature:
161 return "temp"s + std::to_string(sysfsChannelNr) + "_input";
162 case SensorType::Current:
163 return "curr"s + std::to_string(sysfsChannelNr) + "_input";
164 case SensorType::Power:
165 return "power"s + std::to_string(sysfsChannelNr) + "_input";
166 case SensorType::VoltageAC:
167 case SensorType::VoltageDC:
168 return "in"s + std::to_string(sysfsChannelNr) + "_input";
Václav Kubernát6c17d0a2021-03-29 04:55:31 +0200169 }
170
171 __builtin_unreachable();
172}
173
Tomáš Pecka77e09c22023-05-04 20:39:57 +0200174template <SensorType TYPE>
175const DataTree sysfsStaticData;
176template <>
177const DataTree sysfsStaticData<SensorType::Temperature> = {
Václav Kubernát6c17d0a2021-03-29 04:55:31 +0200178 {"class", "iana-hardware:sensor"},
179 {"sensor-data/value-type", "celsius"},
180 {"sensor-data/value-scale", "milli"},
181 {"sensor-data/value-precision", "0"},
182 {"sensor-data/oper-status", "ok"},
183};
Tomáš Pecka77e09c22023-05-04 20:39:57 +0200184template <>
185const DataTree sysfsStaticData<SensorType::Current> = {
Václav Kubernát97e5ea12021-03-24 00:36:57 +0100186 {"class", "iana-hardware:sensor"},
187 {"sensor-data/value-type", "amperes"},
188 {"sensor-data/value-scale", "milli"},
189 {"sensor-data/value-precision", "0"},
190 {"sensor-data/oper-status", "ok"},
191};
Tomáš Pecka77e09c22023-05-04 20:39:57 +0200192template <>
193const DataTree sysfsStaticData<SensorType::Power> = {
Václav Kubernát97e5ea12021-03-24 00:36:57 +0100194 {"class", "iana-hardware:sensor"},
195 {"sensor-data/value-type", "watts"},
196 {"sensor-data/value-scale", "micro"},
197 {"sensor-data/value-precision", "0"},
198 {"sensor-data/oper-status", "ok"},
199};
Tomáš Pecka77e09c22023-05-04 20:39:57 +0200200template <>
201const DataTree sysfsStaticData<SensorType::VoltageAC> = {
Václav Kubernát97e5ea12021-03-24 00:36:57 +0100202 {"class", "iana-hardware:sensor"},
203 {"sensor-data/value-type", "volts-AC"},
Tomáš Pecka76323602022-05-11 09:33:59 +0200204 {"sensor-data/value-scale", "milli"},
Václav Kubernát97e5ea12021-03-24 00:36:57 +0100205 {"sensor-data/value-precision", "0"},
Tomáš Pecka77e09c22023-05-04 20:39:57 +0200206 {"sensor-data/oper-status", "ok"}};
207template <>
208const DataTree sysfsStaticData<SensorType::VoltageDC> = {
Václav Kubernát97e5ea12021-03-24 00:36:57 +0100209 {"class", "iana-hardware:sensor"},
210 {"sensor-data/value-type", "volts-DC"},
Tomáš Pecka76323602022-05-11 09:33:59 +0200211 {"sensor-data/value-scale", "milli"},
Václav Kubernát97e5ea12021-03-24 00:36:57 +0100212 {"sensor-data/value-precision", "0"},
Tomáš Pecka77e09c22023-05-04 20:39:57 +0200213 {"sensor-data/oper-status", "ok"}};
Václav Kubernát6c17d0a2021-03-29 04:55:31 +0200214
215template <SensorType TYPE>
Tomáš Pecka4886db22023-05-10 10:46:15 +0200216SysfsValue<TYPE>::SysfsValue(std::string componentName, std::optional<std::string> parent, std::shared_ptr<sysfs::HWMon> hwmon, int sysfsChannelNr, Thresholds<int64_t> thresholds)
Tomáš Pecka339bc672020-11-11 15:59:03 +0100217 : DataReader(std::move(componentName), std::move(parent))
218 , m_hwmon(std::move(hwmon))
Václav Kubernát6c17d0a2021-03-29 04:55:31 +0200219 , m_sysfsFile(getSysfsFilename(TYPE, sysfsChannelNr))
Tomáš Pecka4886db22023-05-10 10:46:15 +0200220 , m_thresholds(std::move(thresholds))
Tomáš Pecka339bc672020-11-11 15:59:03 +0100221{
222 addComponent(m_staticData,
223 m_componentName,
224 m_parent,
Václav Kubernát6c17d0a2021-03-29 04:55:31 +0200225 sysfsStaticData<TYPE>);
Tomáš Pecka339bc672020-11-11 15:59:03 +0100226}
227
Václav Kubernát6c17d0a2021-03-29 04:55:31 +0200228template <SensorType TYPE>
229DataTree SysfsValue<TYPE>::operator()() const
Tomáš Pecka339bc672020-11-11 15:59:03 +0100230{
231 DataTree res(m_staticData);
232
Václav Kubernát0dee6b92021-04-13 09:14:04 +0200233 int64_t sensorValue = m_hwmon->attribute(m_sysfsFile);
Tomáš Pecka339bc672020-11-11 15:59:03 +0100234 addSensorValue(res, m_componentName, std::to_string(sensorValue));
235
236 return res;
237}
238
Tomáš Pecka4886db22023-05-10 10:46:15 +0200239template <SensorType TYPE>
240ThresholdsBySensorPath SysfsValue<TYPE>::thresholds() const
241{
242 return {{xpathForComponent(m_componentName) + "sensor-data/value", m_thresholds}};
243}
244
Václav Kubernát97e5ea12021-03-24 00:36:57 +0100245template struct SysfsValue<SensorType::Current>;
246template struct SysfsValue<SensorType::Power>;
Václav Kubernát6c17d0a2021-03-29 04:55:31 +0200247template struct SysfsValue<SensorType::Temperature>;
Václav Kubernát97e5ea12021-03-24 00:36:57 +0100248template struct SysfsValue<SensorType::VoltageAC>;
249template struct SysfsValue<SensorType::VoltageDC>;
Václav Kubernát6c17d0a2021-03-29 04:55:31 +0200250
Tomáš Pecka4886db22023-05-10 10:46:15 +0200251EMMC::EMMC(std::string componentName, std::optional<std::string> parent, std::shared_ptr<sysfs::EMMC> emmc, Thresholds<int64_t> thresholds)
Tomáš Pecka339bc672020-11-11 15:59:03 +0100252 : DataReader(std::move(componentName), std::move(parent))
253 , m_emmc(std::move(emmc))
Tomáš Pecka4886db22023-05-10 10:46:15 +0200254 , m_thresholds(std::move(thresholds))
Tomáš Pecka339bc672020-11-11 15:59:03 +0100255{
256 auto emmcAttrs = m_emmc->attributes();
257
258 // date is specified in MM/YYYY format (source: kernel core/mmc.c) and mfg-date is unfortunately of type yang:date-and-time
259 std::string mfgDate = emmcAttrs.at("date");
Tomáš Peckaf1cba742023-05-03 16:26:37 +0200260 std::chrono::year_month_day calendarDate(
261 std::chrono::year(std::stoi(mfgDate.substr(3, 4))),
262 std::chrono::month(std::stoi(mfgDate.substr(0, 2))),
263 std::chrono::day(1));
264 mfgDate = velia::utils::yangTimeFormat(std::chrono::sys_days{calendarDate});
Tomáš Pecka339bc672020-11-11 15:59:03 +0100265
266 addComponent(m_staticData,
267 m_componentName,
268 m_parent,
Tomáš Pecka77e09c22023-05-04 20:39:57 +0200269 DataTree{
Tomáš Pecka339bc672020-11-11 15:59:03 +0100270 {"class", "iana-hardware:module"},
271 {"mfg-date", mfgDate},
272 {"serial-num", emmcAttrs.at("serial")},
273 {"model-name", emmcAttrs.at("name")},
274 });
275
276 addComponent(m_staticData,
277 m_componentName + ":lifetime",
278 m_componentName,
Tomáš Pecka77e09c22023-05-04 20:39:57 +0200279 DataTree{
Tomáš Pecka339bc672020-11-11 15:59:03 +0100280 {"class", "iana-hardware:sensor"},
281 {"sensor-data/value-type", "other"},
282 {"sensor-data/value-scale", "units"},
283 {"sensor-data/value-precision", "0"},
284 {"sensor-data/oper-status", "ok"},
285 {"sensor-data/units-display", "percent"s},
286 });
287}
288
289DataTree EMMC::operator()() const
290{
291 DataTree res(m_staticData);
292
293 auto emmcAttrs = m_emmc->attributes();
294 addSensorValue(res, m_componentName + ":lifetime", emmcAttrs.at("life_time"));
295
296 return res;
297}
Tomáš Pecka2a4c9f62023-03-26 10:54:57 +0200298
Tomáš Pecka4886db22023-05-10 10:46:15 +0200299ThresholdsBySensorPath EMMC::thresholds() const
Tomáš Pecka2a4c9f62023-03-26 10:54:57 +0200300{
Tomáš Pecka4886db22023-05-10 10:46:15 +0200301 return {{xpathForComponent(m_componentName + ":lifetime") + "sensor-data/value", m_thresholds}};
Tomáš Pecka2a4c9f62023-03-26 10:54:57 +0200302}
303
304DataTree Group::operator()() const
305{
306 DataTree res;
307 for (const auto& reader : m_readers) {
308 res.merge(reader());
309 }
310 return res;
311}
312
Tomáš Pecka4886db22023-05-10 10:46:15 +0200313ThresholdsBySensorPath Group::thresholds() const
314{
315 return m_thresholds;
316}
Tomáš Pecka339bc672020-11-11 15:59:03 +0100317}
318}