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