xref: /openbmc/telemetry/src/trigger_actions.cpp (revision c1dbac16fe3c705b773155f607a4541cf2ad79fa)
1 #include "trigger_actions.hpp"
2 
3 #include "messages/update_report_ind.hpp"
4 #include "types/trigger_types.hpp"
5 #include "utils/clock.hpp"
6 #include "utils/messanger.hpp"
7 #include "utils/to_short_enum.hpp"
8 
9 #include <phosphor-logging/log.hpp>
10 
11 #include <ctime>
12 #include <iomanip>
13 #include <sstream>
14 
15 namespace action
16 {
17 
18 namespace
19 {
timestampToString(Milliseconds timestamp)20 std::string timestampToString(Milliseconds timestamp)
21 {
22     std::time_t t = static_cast<time_t>(
23         std::chrono::duration_cast<std::chrono::seconds>(timestamp).count());
24     std::stringstream ss;
25     ss << std::put_time(std::gmtime(&t), "%FT%T.") << std::setw(3)
26        << std::setfill('0') << timestamp.count() % 1000 << 'Z';
27     return ss.str();
28 }
29 } // namespace
30 
31 namespace numeric
32 {
33 
getDirection(double value,double threshold)34 static const char* getDirection(double value, double threshold)
35 {
36     if (value < threshold)
37     {
38         return "decreasing";
39     }
40     if (value > threshold)
41     {
42         return "increasing";
43     }
44     throw std::runtime_error("Invalid value");
45 }
46 
commit(const std::string & triggerId,const ThresholdName thresholdNameInIn,const std::string & sensorName,const Milliseconds timestamp,const TriggerValue triggerValue)47 void LogToJournal::commit(
48     const std::string& triggerId, const ThresholdName thresholdNameInIn,
49     const std::string& sensorName, const Milliseconds timestamp,
50     const TriggerValue triggerValue)
51 {
52     double value = std::get<double>(triggerValue);
53     std::string thresholdName = ::numeric::typeToString(type);
54     auto direction = getDirection(value, threshold);
55 
56     std::string msg =
57         "Numeric threshold '" + std::string(utils::toShortEnum(thresholdName)) +
58         "' of trigger '" + triggerId + "' is crossed on sensor " + sensorName +
59         ", recorded value: " + std::to_string(value) +
60         ", crossing direction: " + std::string(utils::toShortEnum(direction)) +
61         ", timestamp: " + timestampToString(timestamp);
62 
63     phosphor::logging::log<phosphor::logging::level::INFO>(msg.c_str());
64 }
65 
getRedfishMessageId(const double value) const66 const char* LogToRedfishEventLog::getRedfishMessageId(const double value) const
67 {
68     std::string direction(getDirection(value, threshold));
69 
70     if (direction == "decreasing")
71     {
72         switch (type)
73         {
74             case ::numeric::Type::upperCritical:
75                 return redfish_message_ids::TriggerNumericBelowUpperCritical;
76             case ::numeric::Type::lowerCritical:
77                 return redfish_message_ids::TriggerNumericBelowLowerCritical;
78             case ::numeric::Type::upperWarning:
79                 return redfish_message_ids::TriggerNumericReadingNormal;
80             case ::numeric::Type::lowerWarning:
81                 return redfish_message_ids::TriggerNumericBelowLowerWarning;
82         }
83     }
84 
85     if (direction == "increasing")
86     {
87         switch (type)
88         {
89             case ::numeric::Type::upperCritical:
90                 return redfish_message_ids::TriggerNumericAboveUpperCritical;
91             case ::numeric::Type::lowerCritical:
92                 return redfish_message_ids::TriggerNumericAboveLowerCritical;
93             case ::numeric::Type::upperWarning:
94                 return redfish_message_ids::TriggerNumericAboveUpperWarning;
95             case ::numeric::Type::lowerWarning:
96                 return redfish_message_ids::TriggerNumericReadingNormal;
97         }
98     }
99 
100     throw std::runtime_error("Invalid type");
101 }
102 
commit(const std::string & triggerId,const ThresholdName thresholdNameInIn,const std::string & sensorName,const Milliseconds timestamp,const TriggerValue triggerValue)103 void LogToRedfishEventLog::commit(
104     const std::string& triggerId, const ThresholdName thresholdNameInIn,
105     const std::string& sensorName, const Milliseconds timestamp,
106     const TriggerValue triggerValue)
107 {
108     double value = std::get<double>(triggerValue);
109     auto messageId = getRedfishMessageId(value);
110 
111     if (std::string_view(messageId) ==
112         redfish_message_ids::TriggerNumericReadingNormal)
113     {
114         phosphor::logging::log<phosphor::logging::level::INFO>(
115             "Logging numeric trigger action to Redfish Event Log.",
116             phosphor::logging::entry("REDFISH_MESSAGE_ID=%s", messageId),
117             phosphor::logging::entry("REDFISH_MESSAGE_ARGS=%s,%f,%s",
118                                      sensorName.c_str(), value,
119                                      triggerId.c_str()));
120     }
121     else
122     {
123         phosphor::logging::log<phosphor::logging::level::INFO>(
124             "Logging numeric trigger action to Redfish Event Log.",
125             phosphor::logging::entry("REDFISH_MESSAGE_ID=%s", messageId),
126             phosphor::logging::entry("REDFISH_MESSAGE_ARGS=%s,%f,%f,%s",
127                                      sensorName.c_str(), value, threshold,
128                                      triggerId.c_str()));
129     }
130 }
131 
fillActions(std::vector<std::unique_ptr<interfaces::TriggerAction>> & actionsIf,const std::vector<TriggerAction> & ActionsEnum,::numeric::Type type,double thresholdValue,boost::asio::io_context & ioc,const std::shared_ptr<std::vector<std::string>> & reportIds)132 void fillActions(
133     std::vector<std::unique_ptr<interfaces::TriggerAction>>& actionsIf,
134     const std::vector<TriggerAction>& ActionsEnum, ::numeric::Type type,
135     double thresholdValue, boost::asio::io_context& ioc,
136     const std::shared_ptr<std::vector<std::string>>& reportIds)
137 {
138     actionsIf.reserve(ActionsEnum.size());
139     for (auto actionType : ActionsEnum)
140     {
141         switch (actionType)
142         {
143             case TriggerAction::LogToJournal:
144             {
145                 actionsIf.emplace_back(
146                     std::make_unique<LogToJournal>(type, thresholdValue));
147                 break;
148             }
149             case TriggerAction::LogToRedfishEventLog:
150             {
151                 actionsIf.emplace_back(std::make_unique<LogToRedfishEventLog>(
152                     type, thresholdValue));
153                 break;
154             }
155             case TriggerAction::UpdateReport:
156             {
157                 actionsIf.emplace_back(
158                     std::make_unique<UpdateReport>(ioc, reportIds));
159                 break;
160             }
161         }
162     }
163 }
164 
165 } // namespace numeric
166 
167 namespace discrete
168 {
169 
commit(const std::string & triggerId,const ThresholdName thresholdNameIn,const std::string & sensorName,const Milliseconds timestamp,const TriggerValue triggerValue)170 void LogToJournal::commit(
171     const std::string& triggerId, const ThresholdName thresholdNameIn,
172     const std::string& sensorName, const Milliseconds timestamp,
173     const TriggerValue triggerValue)
174 {
175     auto value = std::get<std::string>(triggerValue);
176 
177     std::string msg =
178         "Discrete condition '" + thresholdNameIn->get() + "' of trigger '" +
179         triggerId + "' is met on sensor " + sensorName +
180         ", recorded value: " + value + ", severity: " +
181         std::string(utils::toShortEnum(utils::enumToString(severity))) +
182         ", timestamp: " + timestampToString(timestamp);
183 
184     phosphor::logging::log<phosphor::logging::level::INFO>(msg.c_str());
185 }
186 
commit(const std::string & triggerId,const ThresholdName thresholdNameIn,const std::string & sensorName,const Milliseconds timestamp,const TriggerValue triggerValue)187 void LogToRedfishEventLog::commit(
188     const std::string& triggerId, const ThresholdName thresholdNameIn,
189     const std::string& sensorName, const Milliseconds timestamp,
190     const TriggerValue triggerValue)
191 {
192     auto value = std::get<std::string>(triggerValue);
193 
194     phosphor::logging::log<phosphor::logging::level::INFO>(
195         "Logging discrete trigger action to Redfish Event Log.",
196         phosphor::logging::entry(
197             "REDFISH_MESSAGE_ID=%s",
198             redfish_message_ids::TriggerDiscreteConditionMet),
199         phosphor::logging::entry("REDFISH_MESSAGE_ARGS=%s,%s,%s",
200                                  sensorName.c_str(), value.c_str(),
201                                  triggerId.c_str()));
202 }
203 
fillActions(std::vector<std::unique_ptr<interfaces::TriggerAction>> & actionsIf,const std::vector<TriggerAction> & ActionsEnum,::discrete::Severity severity,boost::asio::io_context & ioc,const std::shared_ptr<std::vector<std::string>> & reportIds)204 void fillActions(
205     std::vector<std::unique_ptr<interfaces::TriggerAction>>& actionsIf,
206     const std::vector<TriggerAction>& ActionsEnum,
207     ::discrete::Severity severity, boost::asio::io_context& ioc,
208     const std::shared_ptr<std::vector<std::string>>& reportIds)
209 {
210     actionsIf.reserve(ActionsEnum.size());
211     for (auto actionType : ActionsEnum)
212     {
213         switch (actionType)
214         {
215             case TriggerAction::LogToJournal:
216             {
217                 actionsIf.emplace_back(
218                     std::make_unique<LogToJournal>(severity));
219                 break;
220             }
221             case TriggerAction::LogToRedfishEventLog:
222             {
223                 actionsIf.emplace_back(
224                     std::make_unique<LogToRedfishEventLog>());
225                 break;
226             }
227             case TriggerAction::UpdateReport:
228             {
229                 actionsIf.emplace_back(
230                     std::make_unique<UpdateReport>(ioc, reportIds));
231                 break;
232             }
233         }
234     }
235 }
236 
237 namespace onChange
238 {
commit(const std::string & triggerId,const ThresholdName thresholdNameIn,const std::string & sensorName,const Milliseconds timestamp,const TriggerValue triggerValue)239 void LogToJournal::commit(
240     const std::string& triggerId, const ThresholdName thresholdNameIn,
241     const std::string& sensorName, const Milliseconds timestamp,
242     const TriggerValue triggerValue)
243 {
244     auto value = triggerValueToString(triggerValue);
245     std::string msg =
246         "Discrete condition 'OnChange' of trigger '" + triggerId +
247         "' is met on sensor: " + sensorName + ", recorded value: " + value +
248         ", timestamp: " + timestampToString(timestamp);
249 
250     phosphor::logging::log<phosphor::logging::level::INFO>(msg.c_str());
251 }
252 
commit(const std::string & triggerId,const ThresholdName thresholdNameIn,const std::string & sensorName,const Milliseconds timestamp,const TriggerValue triggerValue)253 void LogToRedfishEventLog::commit(
254     const std::string& triggerId, const ThresholdName thresholdNameIn,
255     const std::string& sensorName, const Milliseconds timestamp,
256     const TriggerValue triggerValue)
257 {
258     auto value = triggerValueToString(triggerValue);
259 
260     phosphor::logging::log<phosphor::logging::level::INFO>(
261         "Logging onChange discrete trigger action to Redfish Event Log.",
262         phosphor::logging::entry(
263             "REDFISH_MESSAGE_ID=%s",
264             redfish_message_ids::TriggerDiscreteConditionMet),
265         phosphor::logging::entry("REDFISH_MESSAGE_ARGS=%s,%s,%s",
266                                  sensorName.c_str(), value.c_str(),
267                                  triggerId.c_str()));
268 }
269 
fillActions(std::vector<std::unique_ptr<interfaces::TriggerAction>> & actionsIf,const std::vector<TriggerAction> & ActionsEnum,boost::asio::io_context & ioc,const std::shared_ptr<std::vector<std::string>> & reportIds)270 void fillActions(
271     std::vector<std::unique_ptr<interfaces::TriggerAction>>& actionsIf,
272     const std::vector<TriggerAction>& ActionsEnum, boost::asio::io_context& ioc,
273     const std::shared_ptr<std::vector<std::string>>& reportIds)
274 {
275     actionsIf.reserve(ActionsEnum.size());
276     for (auto actionType : ActionsEnum)
277     {
278         switch (actionType)
279         {
280             case TriggerAction::LogToJournal:
281             {
282                 actionsIf.emplace_back(std::make_unique<LogToJournal>());
283                 break;
284             }
285             case TriggerAction::LogToRedfishEventLog:
286             {
287                 actionsIf.emplace_back(
288                     std::make_unique<LogToRedfishEventLog>());
289                 break;
290             }
291             case TriggerAction::UpdateReport:
292             {
293                 actionsIf.emplace_back(
294                     std::make_unique<UpdateReport>(ioc, reportIds));
295                 break;
296             }
297         }
298     }
299 }
300 } // namespace onChange
301 } // namespace discrete
302 
commit(const std::string & triggerId,const ThresholdName thresholdNameIn,const std::string & sensorName,const Milliseconds timestamp,const TriggerValue triggerValue)303 void UpdateReport::commit(
304     const std::string& triggerId, const ThresholdName thresholdNameIn,
305     const std::string& sensorName, const Milliseconds timestamp,
306     const TriggerValue triggerValue)
307 {
308     if (reportIds->empty())
309     {
310         return;
311     }
312 
313     utils::Messanger messanger(ioc);
314     messanger.send(messages::UpdateReportInd{*reportIds});
315 }
316 } // namespace action
317