blob: 24cd0670d672f085f12457fb68df5623e6f1f259 [file] [log] [blame]
Vijay Khemkae2795302020-07-15 17:28:45 -07001#include "config.h"
2
3#include "healthMonitor.hpp"
4
Sui Chen036f1612021-07-22 01:31:49 -07005#include <unistd.h>
6
7#include <boost/asio/deadline_timer.hpp>
8#include <sdbusplus/asio/connection.hpp>
9#include <sdbusplus/asio/object_server.hpp>
10#include <sdbusplus/asio/sd_event.hpp>
11#include <sdbusplus/bus/match.hpp>
Vijay Khemka1d0d0122020-09-29 12:17:43 -070012#include <sdbusplus/server/manager.hpp>
Vijay Khemkae2795302020-07-15 17:28:45 -070013#include <sdeventplus/event.hpp>
14
15#include <fstream>
16#include <iostream>
Sui Chen036f1612021-07-22 01:31:49 -070017#include <memory>
Vijay Khemka15537762020-07-22 11:44:56 -070018#include <numeric>
19#include <sstream>
20
21extern "C"
22{
Bruceleequantatwaf9acbd2020-10-12 15:21:42 +080023#include <sys/statvfs.h>
Vijay Khemka15537762020-07-22 11:44:56 -070024#include <sys/sysinfo.h>
25}
Vijay Khemkae2795302020-07-15 17:28:45 -070026
Patrick Williams957e03c2021-09-02 16:38:42 -050027PHOSPHOR_LOG2_USING;
28
Vijay Khemkae2795302020-07-15 17:28:45 -070029static constexpr bool DEBUG = false;
Vijay Khemka415dcd22020-09-21 15:58:21 -070030static constexpr uint8_t defaultHighThreshold = 100;
Vijay Khemkae2795302020-07-15 17:28:45 -070031
Sui Chen036f1612021-07-22 01:31:49 -070032// Limit sensor recreation interval to 10s
33bool needUpdate;
34static constexpr int TIMER_INTERVAL = 10;
35std::shared_ptr<boost::asio::deadline_timer> sensorRecreateTimer;
36std::shared_ptr<phosphor::health::HealthMon> healthMon;
37
Vijay Khemkae2795302020-07-15 17:28:45 -070038namespace phosphor
39{
40namespace health
41{
42
Sui Chen517524a2021-12-19 20:52:46 -080043// Example values for iface:
44// BMC_CONFIGURATION
45// BMC_INVENTORY_ITEM
46std::vector<std::string> findPathsWithType(sdbusplus::bus::bus& bus,
47 const std::string& iface)
48{
49 PHOSPHOR_LOG2_USING;
50 std::vector<std::string> ret;
51
52 // Find all BMCs (DBus objects implementing the
53 // Inventory.Item.Bmc interface that may be created by
54 // configuring the Inventory Manager)
55 sdbusplus::message::message msg = bus.new_method_call(
56 "xyz.openbmc_project.ObjectMapper",
57 "/xyz/openbmc_project/object_mapper",
58 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths");
59
60 // "/": No limit for paths for all the paths that may be touched
61 // in this daemon
62
63 // 0: Limit the depth to 0 to match both objects created by
64 // EntityManager and by InventoryManager
65
66 // {iface}: The endpoint of the Association Definition must have
67 // the Inventory.Item.Bmc interface
68 msg.append("/", 0, std::vector<std::string>{iface});
69
70 try
71 {
72 bus.call(msg, 0).read(ret);
73
74 if (!ret.empty())
75 {
76 debug("{IFACE} found", "IFACE", iface);
77 }
78 else
79 {
80 debug("{IFACE} not found", "IFACE", iface);
81 }
82 }
83 catch (std::exception& e)
84 {
85 error("Exception occurred while calling {PATH}: {ERROR}", "PATH",
86 InventoryPath, "ERROR", e);
87 }
88 return ret;
89}
90
Vijay Khemka15537762020-07-22 11:44:56 -070091enum CPUStatesTime
92{
93 USER_IDX = 0,
94 NICE_IDX,
95 SYSTEM_IDX,
96 IDLE_IDX,
97 IOWAIT_IDX,
98 IRQ_IDX,
99 SOFTIRQ_IDX,
100 STEAL_IDX,
101 GUEST_USER_IDX,
102 GUEST_NICE_IDX,
103 NUM_CPU_STATES_TIME
104};
105
Patrick Williams957e03c2021-09-02 16:38:42 -0500106double readCPUUtilization([[maybe_unused]] std::string path)
Vijay Khemka15537762020-07-22 11:44:56 -0700107{
Patrick Williams957e03c2021-09-02 16:38:42 -0500108 auto proc_stat = "/proc/stat";
109 std::ifstream fileStat(proc_stat);
Vijay Khemka15537762020-07-22 11:44:56 -0700110 if (!fileStat.is_open())
111 {
Patrick Williams957e03c2021-09-02 16:38:42 -0500112 error("cpu file not available: {PATH}", "PATH", proc_stat);
Vijay Khemka15537762020-07-22 11:44:56 -0700113 return -1;
114 }
115
116 std::string firstLine, labelName;
117 std::size_t timeData[NUM_CPU_STATES_TIME];
118
119 std::getline(fileStat, firstLine);
120 std::stringstream ss(firstLine);
121 ss >> labelName;
122
123 if (DEBUG)
124 std::cout << "CPU stats first Line is " << firstLine << "\n";
125
126 if (labelName.compare("cpu"))
127 {
Patrick Williams957e03c2021-09-02 16:38:42 -0500128 error("CPU data not available");
Vijay Khemka15537762020-07-22 11:44:56 -0700129 return -1;
130 }
131
132 int i;
133 for (i = 0; i < NUM_CPU_STATES_TIME; i++)
134 {
135 if (!(ss >> timeData[i]))
136 break;
137 }
138
139 if (i != NUM_CPU_STATES_TIME)
140 {
Patrick Williams957e03c2021-09-02 16:38:42 -0500141 error("CPU data not correct");
Vijay Khemka15537762020-07-22 11:44:56 -0700142 return -1;
143 }
144
145 static double preActiveTime = 0, preIdleTime = 0;
146 double activeTime, activeTimeDiff, idleTime, idleTimeDiff, totalTime,
147 activePercValue;
148
149 idleTime = timeData[IDLE_IDX] + timeData[IOWAIT_IDX];
150 activeTime = timeData[USER_IDX] + timeData[NICE_IDX] +
151 timeData[SYSTEM_IDX] + timeData[IRQ_IDX] +
152 timeData[SOFTIRQ_IDX] + timeData[STEAL_IDX] +
153 timeData[GUEST_USER_IDX] + timeData[GUEST_NICE_IDX];
154
155 idleTimeDiff = idleTime - preIdleTime;
156 activeTimeDiff = activeTime - preActiveTime;
157
158 /* Store current idle and active time for next calculation */
159 preIdleTime = idleTime;
160 preActiveTime = activeTime;
161
162 totalTime = idleTimeDiff + activeTimeDiff;
163
164 activePercValue = activeTimeDiff / totalTime * 100;
165
166 if (DEBUG)
167 std::cout << "CPU Utilization is " << activePercValue << "\n";
168
169 return activePercValue;
170}
171
Sui Chen517524a2021-12-19 20:52:46 -0800172double readMemoryUtilization([[maybe_unused]] const std::string& path)
Vijay Khemka15537762020-07-22 11:44:56 -0700173{
Bruceleequantatwaf9acbd2020-10-12 15:21:42 +0800174 /* Unused var: path */
175 std::ignore = path;
Potin Laib7d7bd52022-08-23 01:47:13 +0000176 std::ifstream meminfo("/proc/meminfo");
177 std::string line;
178 double memTotal = -1;
179 double memAvail = -1;
Vijay Khemka15537762020-07-22 11:44:56 -0700180
Potin Laib7d7bd52022-08-23 01:47:13 +0000181 while (std::getline(meminfo, line))
182 {
183 std::string name;
184 double value;
185 std::istringstream iss(line);
186
187 if (!(iss >> name >> value))
188 {
189 continue;
190 }
191
192 if (name.starts_with("MemTotal"))
193 {
194 memTotal = value;
195 }
196 else if (name.starts_with("MemAvailable"))
197 {
198 memAvail = value;
199 }
200 }
201
202 if (memTotal <= 0 || memAvail <= 0)
203 {
204 return std::numeric_limits<double>::quiet_NaN();
205 }
Vijay Khemka15537762020-07-22 11:44:56 -0700206
207 if (DEBUG)
208 {
Potin Laib7d7bd52022-08-23 01:47:13 +0000209 std::cout << "MemTotal: " << memTotal << " MemAvailable: " << memAvail
210 << std::endl;
Vijay Khemka15537762020-07-22 11:44:56 -0700211 }
212
Potin Laib7d7bd52022-08-23 01:47:13 +0000213 return (memTotal - memAvail) / memTotal * 100;
Vijay Khemka15537762020-07-22 11:44:56 -0700214}
215
Sui Chen517524a2021-12-19 20:52:46 -0800216double readStorageUtilization([[maybe_unused]] const std::string& path)
Bruceleequantatwaf9acbd2020-10-12 15:21:42 +0800217{
Bruceleequantatwaf9acbd2020-10-12 15:21:42 +0800218 struct statvfs buffer
219 {};
220 int ret = statvfs(path.c_str(), &buffer);
221 double total = 0;
222 double available = 0;
223 double used = 0;
224 double usedPercentage = 0;
225
226 if (ret != 0)
227 {
228 auto e = errno;
Bruceleequantatw2b231e82020-11-23 13:23:45 +0800229 std::cerr << "Error from statvfs: " << strerror(e) << ",path: " << path
230 << std::endl;
Bruceleequantatwaf9acbd2020-10-12 15:21:42 +0800231 return 0;
232 }
233
234 total = buffer.f_blocks * (buffer.f_frsize / 1024);
235 available = buffer.f_bfree * (buffer.f_frsize / 1024);
236 used = total - available;
237 usedPercentage = (used / total) * 100;
238
239 if (DEBUG)
240 {
241 std::cout << "Total:" << total << "\n";
242 std::cout << "Available:" << available << "\n";
243 std::cout << "Used:" << used << "\n";
244 std::cout << "Storage utilization is:" << usedPercentage << "\n";
245 }
246
247 return usedPercentage;
248}
249
Sui Chen517524a2021-12-19 20:52:46 -0800250double readInodeUtilization([[maybe_unused]] const std::string& path)
Bruceleequantatwaf9acbd2020-10-12 15:21:42 +0800251{
Bruceleequantatwaf9acbd2020-10-12 15:21:42 +0800252 struct statvfs buffer
253 {};
254 int ret = statvfs(path.c_str(), &buffer);
255 double totalInodes = 0;
256 double availableInodes = 0;
257 double used = 0;
258 double usedPercentage = 0;
259
260 if (ret != 0)
261 {
262 auto e = errno;
Bruceleequantatw2b231e82020-11-23 13:23:45 +0800263 std::cerr << "Error from statvfs: " << strerror(e) << ",path: " << path
264 << std::endl;
Bruceleequantatwaf9acbd2020-10-12 15:21:42 +0800265 return 0;
266 }
267
268 totalInodes = buffer.f_files;
269 availableInodes = buffer.f_ffree;
270 used = totalInodes - availableInodes;
271 usedPercentage = (used / totalInodes) * 100;
272
273 if (DEBUG)
274 {
275 std::cout << "Total Inodes:" << totalInodes << "\n";
276 std::cout << "Available Inodes:" << availableInodes << "\n";
277 std::cout << "Used:" << used << "\n";
278 std::cout << "Inodes utilization is:" << usedPercentage << "\n";
279 }
280
281 return usedPercentage;
282}
283
284constexpr auto storage = "Storage";
285constexpr auto inode = "Inode";
Vijay Khemka15537762020-07-22 11:44:56 -0700286/** Map of read function for each health sensors supported */
Sui Chen517524a2021-12-19 20:52:46 -0800287const std::map<std::string, std::function<double(const std::string& path)>>
Bruceleequantatwaf9acbd2020-10-12 15:21:42 +0800288 readSensors = {{"CPU", readCPUUtilization},
289 {"Memory", readMemoryUtilization},
290 {storage, readStorageUtilization},
291 {inode, readInodeUtilization}};
Vijay Khemka15537762020-07-22 11:44:56 -0700292
293void HealthSensor::setSensorThreshold(double criticalHigh, double warningHigh)
Vijay Khemkae2795302020-07-15 17:28:45 -0700294{
295 CriticalInterface::criticalHigh(criticalHigh);
Yong Lif8d79732021-03-12 09:12:19 +0800296 CriticalInterface::criticalLow(std::numeric_limits<double>::quiet_NaN());
297
Vijay Khemkae2795302020-07-15 17:28:45 -0700298 WarningInterface::warningHigh(warningHigh);
Yong Lif8d79732021-03-12 09:12:19 +0800299 WarningInterface::warningLow(std::numeric_limits<double>::quiet_NaN());
Vijay Khemkae2795302020-07-15 17:28:45 -0700300}
301
Vijay Khemka15537762020-07-22 11:44:56 -0700302void HealthSensor::setSensorValueToDbus(const double value)
Vijay Khemkae2795302020-07-15 17:28:45 -0700303{
304 ValueIface::value(value);
305}
306
Sui Chen517524a2021-12-19 20:52:46 -0800307void HealthSensor::initHealthSensor(
308 const std::vector<std::string>& bmcInventoryPaths)
Vijay Khemka15537762020-07-22 11:44:56 -0700309{
Sui Chen517524a2021-12-19 20:52:46 -0800310 info("{SENSOR} Health Sensor initialized", "SENSOR", sensorConfig.name);
311
312 /* Look for sensor read functions and Read Sensor values */
313 auto it = readSensors.find(sensorConfig.name);
314
315 if (sensorConfig.name.rfind(storage, 0) == 0)
316 {
317 it = readSensors.find(storage);
318 }
319 else if (sensorConfig.name.rfind(inode, 0) == 0)
320 {
321 it = readSensors.find(inode);
322 }
323 else if (it == readSensors.end())
324 {
325 error("Sensor read function not available");
326 return;
327 }
328
329 double value = it->second(sensorConfig.path);
330
331 if (value < 0)
332 {
333 error("Reading Sensor Utilization failed: {SENSOR}", "SENSOR",
334 sensorConfig.name);
335 return;
336 }
337
338 /* Initialize value queue with initial sensor reading */
339 for (int i = 0; i < sensorConfig.windowSize; i++)
340 {
341 valQueue.push_back(value);
342 }
343
Vijay Khemka08797702020-09-21 14:53:57 -0700344 /* Initialize unit value (Percent) for utilization sensor */
345 ValueIface::unit(ValueIface::Unit::Percent);
346
Konstantin Aladyshev9d29b372021-12-21 15:45:02 +0300347 ValueIface::maxValue(100);
348 ValueIface::minValue(0);
Potin Laic82e6162022-08-02 10:22:56 +0000349 ValueIface::value(std::numeric_limits<double>::quiet_NaN());
Vijay Khemkab38fd582020-07-23 13:21:23 -0700350
Sui Chen670cc132021-04-13 09:27:22 -0700351 // Associate the sensor to chassis
Sui Chen517524a2021-12-19 20:52:46 -0800352 // This connects the DBus object to a Chassis.
353
Sui Chen670cc132021-04-13 09:27:22 -0700354 std::vector<AssociationTuple> associationTuples;
Sui Chen517524a2021-12-19 20:52:46 -0800355 for (const auto& chassisId : bmcInventoryPaths)
Sui Chen670cc132021-04-13 09:27:22 -0700356 {
Sui Chen517524a2021-12-19 20:52:46 -0800357 // This utilization sensor "is monitoring" the BMC with path chassisId.
358 // The chassisId is "monitored_by" this utilization sensor.
359 associationTuples.push_back({"monitors", "monitored_by", chassisId});
Sui Chen670cc132021-04-13 09:27:22 -0700360 }
361 AssociationDefinitionInterface::associations(associationTuples);
362
Vijay Khemkab38fd582020-07-23 13:21:23 -0700363 /* Start the timer for reading sensor data at regular interval */
364 readTimer.restart(std::chrono::milliseconds(sensorConfig.freq * 1000));
365}
366
Vijay Khemkab7a7b8a2020-07-29 12:22:01 -0700367void HealthSensor::checkSensorThreshold(const double value)
368{
Konstantin Aladysheva6cd7042021-12-21 15:36:01 +0300369 if (std::isfinite(sensorConfig.criticalHigh) &&
370 (value > sensorConfig.criticalHigh))
Vijay Khemkab7a7b8a2020-07-29 12:22:01 -0700371 {
372 if (!CriticalInterface::criticalAlarmHigh())
373 {
374 CriticalInterface::criticalAlarmHigh(true);
375 if (sensorConfig.criticalLog)
Potin Lai156ecf32022-07-11 17:09:10 +0800376 {
Patrick Williams957e03c2021-09-02 16:38:42 -0500377 error(
378 "ASSERT: sensor {SENSOR} is above the upper threshold critical high",
379 "SENSOR", sensorConfig.name);
Potin Lai156ecf32022-07-11 17:09:10 +0800380 startUnit(sensorConfig.criticalTgt);
381 }
Vijay Khemkab7a7b8a2020-07-29 12:22:01 -0700382 }
Konstantin Aladysheva6cd7042021-12-21 15:36:01 +0300383 return;
Vijay Khemkab7a7b8a2020-07-29 12:22:01 -0700384 }
Konstantin Aladysheva6cd7042021-12-21 15:36:01 +0300385
386 if (CriticalInterface::criticalAlarmHigh())
Vijay Khemkab7a7b8a2020-07-29 12:22:01 -0700387 {
Konstantin Aladysheva6cd7042021-12-21 15:36:01 +0300388 CriticalInterface::criticalAlarmHigh(false);
389 if (sensorConfig.criticalLog)
390 info(
391 "DEASSERT: sensor {SENSOR} is under the upper threshold critical high",
392 "SENSOR", sensorConfig.name);
393 }
Vijay Khemkab7a7b8a2020-07-29 12:22:01 -0700394
Konstantin Aladysheva6cd7042021-12-21 15:36:01 +0300395 if (std::isfinite(sensorConfig.warningHigh) &&
396 (value > sensorConfig.warningHigh))
397 {
398 if (!WarningInterface::warningAlarmHigh())
Vijay Khemkab7a7b8a2020-07-29 12:22:01 -0700399 {
400 WarningInterface::warningAlarmHigh(true);
401 if (sensorConfig.warningLog)
Potin Lai156ecf32022-07-11 17:09:10 +0800402 {
Patrick Williams957e03c2021-09-02 16:38:42 -0500403 error(
404 "ASSERT: sensor {SENSOR} is above the upper threshold warning high",
405 "SENSOR", sensorConfig.name);
Potin Lai156ecf32022-07-11 17:09:10 +0800406 startUnit(sensorConfig.warningTgt);
407 }
Vijay Khemkab7a7b8a2020-07-29 12:22:01 -0700408 }
Konstantin Aladysheva6cd7042021-12-21 15:36:01 +0300409 return;
410 }
411
412 if (WarningInterface::warningAlarmHigh())
413 {
414 WarningInterface::warningAlarmHigh(false);
415 if (sensorConfig.warningLog)
416 info(
417 "DEASSERT: sensor {SENSOR} is under the upper threshold warning high",
418 "SENSOR", sensorConfig.name);
Vijay Khemkab7a7b8a2020-07-29 12:22:01 -0700419 }
420}
421
Vijay Khemkab38fd582020-07-23 13:21:23 -0700422void HealthSensor::readHealthSensor()
423{
424 /* Read current sensor value */
Bruceleequantatwaf9acbd2020-10-12 15:21:42 +0800425 double value;
426
427 if (sensorConfig.name.rfind(storage, 0) == 0)
428 {
429 value = readSensors.find(storage)->second(sensorConfig.path);
430 }
431 else if (sensorConfig.name.rfind(inode, 0) == 0)
432 {
433 value = readSensors.find(inode)->second(sensorConfig.path);
434 }
435 else
436 {
437 value = readSensors.find(sensorConfig.name)->second(sensorConfig.path);
438 }
439
Vijay Khemkab38fd582020-07-23 13:21:23 -0700440 if (value < 0)
441 {
Patrick Williams957e03c2021-09-02 16:38:42 -0500442 error("Reading Sensor Utilization failed: {SENSOR}", "SENSOR",
443 sensorConfig.name);
Vijay Khemkab38fd582020-07-23 13:21:23 -0700444 return;
445 }
446
447 /* Remove first item from the queue */
Potin Laic82e6162022-08-02 10:22:56 +0000448 if (valQueue.size() >= sensorConfig.windowSize)
449 {
450 valQueue.pop_front();
451 }
Vijay Khemkab38fd582020-07-23 13:21:23 -0700452 /* Add new item at the back */
453 valQueue.push_back(value);
Potin Laic82e6162022-08-02 10:22:56 +0000454 /* Wait until the queue is filled with enough reference*/
455 if (valQueue.size() < sensorConfig.windowSize)
456 {
457 return;
458 }
Vijay Khemkab38fd582020-07-23 13:21:23 -0700459
460 /* Calculate average values for the given window size */
461 double avgValue = 0;
462 avgValue = accumulate(valQueue.begin(), valQueue.end(), avgValue);
463 avgValue = avgValue / sensorConfig.windowSize;
464
465 /* Set this new value to dbus */
466 setSensorValueToDbus(avgValue);
Vijay Khemkab7a7b8a2020-07-29 12:22:01 -0700467
468 /* Check the sensor threshold and log required message */
469 checkSensorThreshold(avgValue);
Vijay Khemka15537762020-07-22 11:44:56 -0700470}
471
Potin Lai156ecf32022-07-11 17:09:10 +0800472void HealthSensor::startUnit(const std::string& sysdUnit)
473{
474 if (sysdUnit.empty())
475 {
476 return;
477 }
478
Patrick Williamsbbfe7182022-07-22 19:26:56 -0500479 sdbusplus::message_t msg = bus.new_method_call(
Potin Lai156ecf32022-07-11 17:09:10 +0800480 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
481 "org.freedesktop.systemd1.Manager", "StartUnit");
482 msg.append(sysdUnit, "replace");
483 bus.call_noreply(msg);
484}
485
Sui Chen036f1612021-07-22 01:31:49 -0700486void HealthMon::recreateSensors()
487{
488 PHOSPHOR_LOG2_USING;
489 healthSensors.clear();
Sui Chen036f1612021-07-22 01:31:49 -0700490
Sui Chen517524a2021-12-19 20:52:46 -0800491 // Find BMC inventory paths and create health sensors
492 std::vector<std::string> bmcInventoryPaths =
493 findPathsWithType(bus, BMC_INVENTORY_ITEM);
494 createHealthSensors(bmcInventoryPaths);
Sui Chen036f1612021-07-22 01:31:49 -0700495}
496
Vijay Khemka15537762020-07-22 11:44:56 -0700497void printConfig(HealthConfig& cfg)
498{
499 std::cout << "Name: " << cfg.name << "\n";
500 std::cout << "Freq: " << (int)cfg.freq << "\n";
501 std::cout << "Window Size: " << (int)cfg.windowSize << "\n";
502 std::cout << "Critical value: " << (int)cfg.criticalHigh << "\n";
503 std::cout << "warning value: " << (int)cfg.warningHigh << "\n";
504 std::cout << "Critical log: " << (int)cfg.criticalLog << "\n";
505 std::cout << "Warning log: " << (int)cfg.warningLog << "\n";
506 std::cout << "Critical Target: " << cfg.criticalTgt << "\n";
507 std::cout << "Warning Target: " << cfg.warningTgt << "\n\n";
Bruceleequantatwaf9acbd2020-10-12 15:21:42 +0800508 std::cout << "Path : " << cfg.path << "\n\n";
Vijay Khemka15537762020-07-22 11:44:56 -0700509}
510
Vijay Khemkae2795302020-07-15 17:28:45 -0700511/* Create dbus utilization sensor object for each configured sensors */
Sui Chen517524a2021-12-19 20:52:46 -0800512void HealthMon::createHealthSensors(
513 const std::vector<std::string>& bmcInventoryPaths)
Vijay Khemkae2795302020-07-15 17:28:45 -0700514{
515 for (auto& cfg : sensorConfigs)
516 {
517 std::string objPath = std::string(HEALTH_SENSOR_PATH) + cfg.name;
Sui Chen517524a2021-12-19 20:52:46 -0800518 auto healthSensor = std::make_shared<HealthSensor>(
519 bus, objPath.c_str(), cfg, bmcInventoryPaths);
Vijay Khemkae2795302020-07-15 17:28:45 -0700520 healthSensors.emplace(cfg.name, healthSensor);
521
Patrick Williams957e03c2021-09-02 16:38:42 -0500522 info("{SENSOR} Health Sensor created", "SENSOR", cfg.name);
Vijay Khemkae2795302020-07-15 17:28:45 -0700523
524 /* Set configured values of crtical and warning high to dbus */
525 healthSensor->setSensorThreshold(cfg.criticalHigh, cfg.warningHigh);
526 }
527}
528
529/** @brief Parsing Health config JSON file */
530Json HealthMon::parseConfigFile(std::string configFile)
531{
532 std::ifstream jsonFile(configFile);
533 if (!jsonFile.is_open())
534 {
Patrick Williams957e03c2021-09-02 16:38:42 -0500535 error("config JSON file not found: {PATH}", "PATH", configFile);
Vijay Khemkae2795302020-07-15 17:28:45 -0700536 }
537
538 auto data = Json::parse(jsonFile, nullptr, false);
539 if (data.is_discarded())
540 {
Patrick Williams957e03c2021-09-02 16:38:42 -0500541 error("config readings JSON parser failure: {PATH}", "PATH",
542 configFile);
Vijay Khemkae2795302020-07-15 17:28:45 -0700543 }
544
545 return data;
546}
547
Vijay Khemkae2795302020-07-15 17:28:45 -0700548void HealthMon::getConfigData(Json& data, HealthConfig& cfg)
549{
550
551 static const Json empty{};
552
Vijay Khemka15537762020-07-22 11:44:56 -0700553 /* Default frerquency of sensor polling is 1 second */
554 cfg.freq = data.value("Frequency", 1);
555
556 /* Default window size sensor queue is 1 */
557 cfg.windowSize = data.value("Window_size", 1);
558
Vijay Khemkae2795302020-07-15 17:28:45 -0700559 auto threshold = data.value("Threshold", empty);
560 if (!threshold.empty())
561 {
562 auto criticalData = threshold.value("Critical", empty);
563 if (!criticalData.empty())
564 {
Vijay Khemka415dcd22020-09-21 15:58:21 -0700565 cfg.criticalHigh =
566 criticalData.value("Value", defaultHighThreshold);
Vijay Khemkae2795302020-07-15 17:28:45 -0700567 cfg.criticalLog = criticalData.value("Log", true);
568 cfg.criticalTgt = criticalData.value("Target", "");
569 }
570 auto warningData = threshold.value("Warning", empty);
571 if (!warningData.empty())
572 {
Vijay Khemka415dcd22020-09-21 15:58:21 -0700573 cfg.warningHigh = warningData.value("Value", defaultHighThreshold);
574 cfg.warningLog = warningData.value("Log", false);
Vijay Khemkae2795302020-07-15 17:28:45 -0700575 cfg.warningTgt = warningData.value("Target", "");
576 }
577 }
Bruceleequantatwaf9acbd2020-10-12 15:21:42 +0800578 cfg.path = data.value("Path", "");
Vijay Khemkae2795302020-07-15 17:28:45 -0700579}
580
Vijay Khemka15537762020-07-22 11:44:56 -0700581std::vector<HealthConfig> HealthMon::getHealthConfig()
Vijay Khemkae2795302020-07-15 17:28:45 -0700582{
583
584 std::vector<HealthConfig> cfgs;
585 HealthConfig cfg;
586 auto data = parseConfigFile(HEALTH_CONFIG_FILE);
587
588 // print values
589 if (DEBUG)
590 std::cout << "Config json data:\n" << data << "\n\n";
591
Bruceleequantatw2b231e82020-11-23 13:23:45 +0800592 /* Get data items from config json data*/
Vijay Khemkae2795302020-07-15 17:28:45 -0700593 for (auto& j : data.items())
594 {
595 auto key = j.key();
Bruceleequantatwaf9acbd2020-10-12 15:21:42 +0800596 /* key need match default value in map readSensors or match the key
597 * start with "Storage" or "Inode" */
Bruceleequantatw2b231e82020-11-23 13:23:45 +0800598 bool isStorageOrInode =
599 (key.rfind(storage, 0) == 0 || key.rfind(inode, 0) == 0);
600 if (readSensors.find(key) != readSensors.end() || isStorageOrInode)
Vijay Khemkae2795302020-07-15 17:28:45 -0700601 {
602 HealthConfig cfg = HealthConfig();
603 cfg.name = j.key();
604 getConfigData(j.value(), cfg);
Bruceleequantatw2b231e82020-11-23 13:23:45 +0800605 if (isStorageOrInode)
606 {
607 struct statvfs buffer
608 {};
609 int ret = statvfs(cfg.path.c_str(), &buffer);
610 if (ret != 0)
611 {
612 auto e = errno;
613 std::cerr << "Error from statvfs: " << strerror(e)
614 << ", name: " << cfg.name
615 << ", path: " << cfg.path
616 << ", please check your settings in config file."
617 << std::endl;
618 continue;
619 }
620 }
Vijay Khemkae2795302020-07-15 17:28:45 -0700621 cfgs.push_back(cfg);
622 if (DEBUG)
623 printConfig(cfg);
624 }
625 else
626 {
Patrick Williams957e03c2021-09-02 16:38:42 -0500627 error("{SENSOR} Health Sensor not supported", "SENSOR", key);
Vijay Khemkae2795302020-07-15 17:28:45 -0700628 }
629 }
630 return cfgs;
631}
632
Sui Chen517524a2021-12-19 20:52:46 -0800633// Two caveats here.
634// 1. The BMC Inventory will only show up by the nearest ObjectMapper polling
635// interval.
636// 2. InterfacesAdded events will are not emitted like they are with E-M.
637void HealthMon::createBmcInventoryIfNotCreated()
638{
639 if (bmcInventory == nullptr)
640 {
641 info("createBmcInventory");
642 bmcInventory = std::make_shared<phosphor::health::BmcInventory>(
643 bus, "/xyz/openbmc_project/inventory/bmc");
644 }
645}
646
647bool HealthMon::bmcInventoryCreated()
648{
649 return bmcInventory != nullptr;
650}
651
Vijay Khemkae2795302020-07-15 17:28:45 -0700652} // namespace health
653} // namespace phosphor
654
Sui Chen517524a2021-12-19 20:52:46 -0800655void sensorRecreateTimerCallback(
656 std::shared_ptr<boost::asio::deadline_timer> timer,
657 sdbusplus::bus::bus& bus)
658{
659 timer->expires_from_now(boost::posix_time::seconds(TIMER_INTERVAL));
660 timer->async_wait([timer, &bus](const boost::system::error_code& ec) {
661 if (ec == boost::asio::error::operation_aborted)
662 {
663 info("sensorRecreateTimer aborted");
664 return;
665 }
666
667 // When Entity-manager is already running
668 if (!needUpdate)
669 {
670 if ((!healthMon->bmcInventoryCreated()) &&
671 (!phosphor::health::findPathsWithType(bus, BMC_CONFIGURATION)
672 .empty()))
673 {
674 healthMon->createBmcInventoryIfNotCreated();
675 needUpdate = true;
676 }
677 }
678 else
679 {
680
681 // If this daemon maintains its own DBus object, we must make sure
682 // the object is registered to ObjectMapper
683 if (phosphor::health::findPathsWithType(bus, BMC_INVENTORY_ITEM)
684 .empty())
685 {
686 info(
687 "BMC inventory item not registered to Object Mapper yet, waiting for next iteration");
688 }
689 else
690 {
691 info(
692 "BMC inventory item registered to Object Mapper, creating sensors now");
693 healthMon->recreateSensors();
694 needUpdate = false;
695 }
696 }
697 sensorRecreateTimerCallback(timer, bus);
698 });
699}
700
Vijay Khemkae2795302020-07-15 17:28:45 -0700701/**
702 * @brief Main
703 */
704int main()
705{
Sui Chen036f1612021-07-22 01:31:49 -0700706 // The io_context is needed for the timer
707 boost::asio::io_context io;
708
709 // DBus connection
710 auto conn = std::make_shared<sdbusplus::asio::connection>(io);
711
712 conn->request_name(HEALTH_BUS_NAME);
713
Vijay Khemkae2795302020-07-15 17:28:45 -0700714 // Get a default event loop
715 auto event = sdeventplus::Event::get_default();
716
Vijay Khemkae2795302020-07-15 17:28:45 -0700717 // Create an health monitor object
Sui Chen036f1612021-07-22 01:31:49 -0700718 healthMon = std::make_shared<phosphor::health::HealthMon>(*conn);
Vijay Khemkae2795302020-07-15 17:28:45 -0700719
Yong Lif8d79732021-03-12 09:12:19 +0800720 // Add object manager through object_server
721 sdbusplus::asio::object_server objectServer(conn);
Vijay Khemka1d0d0122020-09-29 12:17:43 -0700722
Sui Chen036f1612021-07-22 01:31:49 -0700723 sdbusplus::asio::sd_event_wrapper sdEvents(io);
724
725 sensorRecreateTimer = std::make_shared<boost::asio::deadline_timer>(io);
726
727 // If the SystemInventory does not exist: wait for the InterfaceAdded signal
Sui Chen517524a2021-12-19 20:52:46 -0800728 auto interfacesAddedSignalHandler = std::make_unique<
729 sdbusplus::bus::match_t>(
730 static_cast<sdbusplus::bus_t&>(*conn),
731 sdbusplus::bus::match::rules::interfacesAdded(),
732 [conn](sdbusplus::message::message& msg) {
733 using Association =
734 std::tuple<std::string, std::string, std::string>;
735 using InterfacesAdded = std::vector<std::pair<
736 std::string,
737 std::vector<std::pair<
738 std::string, std::variant<std::vector<Association>>>>>>;
739
740 sdbusplus::message::object_path o;
741 InterfacesAdded interfacesAdded;
742
743 try
744 {
Sui Chen036f1612021-07-22 01:31:49 -0700745 msg.read(o);
Sui Chen517524a2021-12-19 20:52:46 -0800746 msg.read(interfacesAdded);
747 }
748 catch (const std::exception& e)
749 {
750 error(
751 "Exception occurred while processing interfacesAdded: {EXCEPTION}",
752 "EXCEPTION", e.what());
753 return;
754 }
755
756 // Ignore any signal coming from health-monitor itself.
757 if (msg.get_sender() == conn->get_unique_name())
758 {
759 return;
760 }
761
762 // Check if the BMC Inventory is in the interfaces created.
763 bool hasBmcConfiguration = false;
764 for (const auto& x : interfacesAdded)
765 {
766 if (x.first == BMC_CONFIGURATION)
Sui Chen036f1612021-07-22 01:31:49 -0700767 {
Sui Chen517524a2021-12-19 20:52:46 -0800768 hasBmcConfiguration = true;
Sui Chen036f1612021-07-22 01:31:49 -0700769 }
Sui Chen517524a2021-12-19 20:52:46 -0800770 }
771
772 if (hasBmcConfiguration)
773 {
774 info(
775 "BMC configuration detected, will create a corresponding Inventory item");
776 healthMon->createBmcInventoryIfNotCreated();
777 needUpdate = true;
778 }
779 });
Sui Chen036f1612021-07-22 01:31:49 -0700780
781 // Start the timer
Sui Chen517524a2021-12-19 20:52:46 -0800782 io.post(
783 [conn]() { sensorRecreateTimerCallback(sensorRecreateTimer, *conn); });
Sui Chen036f1612021-07-22 01:31:49 -0700784 io.run();
Vijay Khemkae2795302020-07-15 17:28:45 -0700785
786 return 0;
787}