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