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