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