xref: /openbmc/telemetry/src/trigger_actions.cpp (revision e6d4887453f0b23b46c012da7cb26f2beb38ef0e)
1 #include "trigger_actions.hpp"
2 
3 #include "messages/update_report_ind.hpp"
4 #include "utils/messanger.hpp"
5 
6 #include <phosphor-logging/log.hpp>
7 
8 #include <ctime>
9 
10 namespace action
11 {
12 
13 namespace
14 {
15 std::string timestampToString(Milliseconds timestamp)
16 {
17     std::time_t t = static_cast<time_t>(timestamp.count());
18     std::array<char, sizeof("YYYY-MM-DDThh:mm:ssZ")> buf = {};
19     size_t size =
20         std::strftime(buf.data(), buf.size(), "%FT%TZ", std::gmtime(&t));
21     if (size == 0)
22     {
23         throw std::runtime_error("Failed to parse timestamp to string");
24     }
25     return std::string(buf.data(), size);
26 }
27 } // namespace
28 
29 namespace numeric
30 {
31 
32 static const char* getDirection(double value, double threshold)
33 {
34     if (value < threshold)
35     {
36         return "decreasing";
37     }
38     if (value > threshold)
39     {
40         return "increasing";
41     }
42     throw std::runtime_error("Invalid value");
43 }
44 
45 const char* LogToJournal::getType() const
46 {
47     switch (type)
48     {
49         case ::numeric::Type::upperCritical:
50             return "UpperCritical";
51         case ::numeric::Type::lowerCritical:
52             return "LowerCritical";
53         case ::numeric::Type::upperWarning:
54             return "UpperWarning";
55         case ::numeric::Type::lowerWarning:
56             return "LowerWarning";
57     }
58     throw std::runtime_error("Invalid type");
59 }
60 
61 void LogToJournal::commit(const std::string& sensorName, Milliseconds timestamp,
62                           double value)
63 {
64     std::string msg = std::string(getType()) +
65                       " numeric threshold condition is met on sensor " +
66                       sensorName + ", recorded value " + std::to_string(value) +
67                       ", timestamp " + timestampToString(timestamp) +
68                       ", direction " +
69                       std::string(getDirection(value, threshold));
70 
71     phosphor::logging::log<phosphor::logging::level::INFO>(msg.c_str());
72 }
73 
74 const char* LogToRedfish::getMessageId() const
75 {
76     switch (type)
77     {
78         case ::numeric::Type::upperCritical:
79             return "OpenBMC.0.1.0.NumericThresholdUpperCritical";
80         case ::numeric::Type::lowerCritical:
81             return "OpenBMC.0.1.0.NumericThresholdLowerCritical";
82         case ::numeric::Type::upperWarning:
83             return "OpenBMC.0.1.0.NumericThresholdUpperWarning";
84         case ::numeric::Type::lowerWarning:
85             return "OpenBMC.0.1.0.NumericThresholdLowerWarning";
86     }
87     throw std::runtime_error("Invalid type");
88 }
89 
90 void LogToRedfish::commit(const std::string& sensorName, Milliseconds timestamp,
91                           double value)
92 {
93     phosphor::logging::log<phosphor::logging::level::INFO>(
94         "Threshold value is exceeded",
95         phosphor::logging::entry("REDFISH_MESSAGE_ID=%s", getMessageId()),
96         phosphor::logging::entry("REDFISH_MESSAGE_ARGS=%s,%f,%llu,%s",
97                                  sensorName.c_str(), value, timestamp.count(),
98                                  getDirection(value, threshold)));
99 }
100 
101 void fillActions(
102     std::vector<std::unique_ptr<interfaces::TriggerAction>>& actionsIf,
103     const std::vector<TriggerAction>& ActionsEnum, ::numeric::Type type,
104     double thresholdValue, boost::asio::io_context& ioc,
105     const std::shared_ptr<std::vector<std::string>>& reportIds)
106 {
107     actionsIf.reserve(ActionsEnum.size());
108     for (auto actionType : ActionsEnum)
109     {
110         switch (actionType)
111         {
112             case TriggerAction::LogToLogService:
113             {
114                 actionsIf.emplace_back(
115                     std::make_unique<LogToJournal>(type, thresholdValue));
116                 break;
117             }
118             case TriggerAction::RedfishEvent:
119             {
120                 actionsIf.emplace_back(
121                     std::make_unique<LogToRedfish>(type, thresholdValue));
122                 break;
123             }
124             case TriggerAction::UpdateReport:
125             {
126                 actionsIf.emplace_back(
127                     std::make_unique<UpdateReport>(ioc, reportIds));
128                 break;
129             }
130         }
131     }
132 }
133 
134 } // namespace numeric
135 
136 namespace discrete
137 {
138 const char* LogToJournal::getSeverity() const
139 {
140     switch (severity)
141     {
142         case ::discrete::Severity::ok:
143             return "OK";
144         case ::discrete::Severity::warning:
145             return "Warning";
146         case ::discrete::Severity::critical:
147             return "Critical";
148     }
149     throw std::runtime_error("Invalid severity");
150 }
151 
152 void LogToJournal::commit(const std::string& sensorName, Milliseconds timestamp,
153                           double value)
154 {
155     std::string msg = std::string(getSeverity()) +
156                       " discrete threshold condition is met on sensor " +
157                       sensorName + ", recorded value " + std::to_string(value) +
158                       ", timestamp " + timestampToString(timestamp);
159 
160     phosphor::logging::log<phosphor::logging::level::INFO>(msg.c_str());
161 }
162 
163 const char* LogToRedfish::getMessageId() const
164 {
165     switch (severity)
166     {
167         case ::discrete::Severity::ok:
168             return "OpenBMC.0.1.0.DiscreteThresholdOk";
169         case ::discrete::Severity::warning:
170             return "OpenBMC.0.1.0.DiscreteThresholdWarning";
171         case ::discrete::Severity::critical:
172             return "OpenBMC.0.1.0.DiscreteThresholdCritical";
173     }
174     throw std::runtime_error("Invalid severity");
175 }
176 
177 void LogToRedfish::commit(const std::string& sensorName, Milliseconds timestamp,
178                           double value)
179 {
180     phosphor::logging::log<phosphor::logging::level::INFO>(
181         "Discrete treshold condition is met",
182         phosphor::logging::entry("REDFISH_MESSAGE_ID=%s", getMessageId()),
183         phosphor::logging::entry("REDFISH_MESSAGE_ARGS=%s,%f,%llu",
184                                  sensorName.c_str(), value, timestamp.count()));
185 }
186 
187 void fillActions(
188     std::vector<std::unique_ptr<interfaces::TriggerAction>>& actionsIf,
189     const std::vector<TriggerAction>& ActionsEnum,
190     ::discrete::Severity severity, boost::asio::io_context& ioc,
191     const std::shared_ptr<std::vector<std::string>>& reportIds)
192 {
193     actionsIf.reserve(ActionsEnum.size());
194     for (auto actionType : ActionsEnum)
195     {
196         switch (actionType)
197         {
198             case TriggerAction::LogToLogService:
199             {
200                 actionsIf.emplace_back(
201                     std::make_unique<LogToJournal>(severity));
202                 break;
203             }
204             case TriggerAction::RedfishEvent:
205             {
206                 actionsIf.emplace_back(
207                     std::make_unique<LogToRedfish>(severity));
208                 break;
209             }
210             case TriggerAction::UpdateReport:
211             {
212                 actionsIf.emplace_back(
213                     std::make_unique<UpdateReport>(ioc, reportIds));
214                 break;
215             }
216         }
217     }
218 }
219 
220 namespace onChange
221 {
222 void LogToJournal::commit(const std::string& sensorName, Milliseconds timestamp,
223                           double value)
224 {
225     std::string msg = "Value changed on sensor " + sensorName +
226                       ", recorded value " + std::to_string(value) +
227                       ", timestamp " + timestampToString(timestamp);
228 
229     phosphor::logging::log<phosphor::logging::level::INFO>(msg.c_str());
230 }
231 
232 void LogToRedfish::commit(const std::string& sensorName, Milliseconds timestamp,
233                           double value)
234 {
235     const char* messageId = "OpenBMC.0.1.0.DiscreteThresholdOnChange";
236     phosphor::logging::log<phosphor::logging::level::INFO>(
237         "Uncondtional discrete threshold triggered",
238         phosphor::logging::entry("REDFISH_MESSAGE_ID=%s", messageId),
239         phosphor::logging::entry("REDFISH_MESSAGE_ARGS=%s,%f,%llu",
240                                  sensorName.c_str(), value, timestamp.count()));
241 }
242 
243 void fillActions(
244     std::vector<std::unique_ptr<interfaces::TriggerAction>>& actionsIf,
245     const std::vector<TriggerAction>& ActionsEnum, boost::asio::io_context& ioc,
246     const std::shared_ptr<std::vector<std::string>>& reportIds)
247 {
248     actionsIf.reserve(ActionsEnum.size());
249     for (auto actionType : ActionsEnum)
250     {
251         switch (actionType)
252         {
253             case TriggerAction::LogToLogService:
254             {
255                 actionsIf.emplace_back(std::make_unique<LogToJournal>());
256                 break;
257             }
258             case TriggerAction::RedfishEvent:
259             {
260                 actionsIf.emplace_back(std::make_unique<LogToRedfish>());
261                 break;
262             }
263             case TriggerAction::UpdateReport:
264             {
265                 actionsIf.emplace_back(
266                     std::make_unique<UpdateReport>(ioc, reportIds));
267                 break;
268             }
269         }
270     }
271 }
272 } // namespace onChange
273 } // namespace discrete
274 
275 void UpdateReport::commit(const std::string&, Milliseconds, double)
276 {
277     if (reportIds->empty())
278     {
279         return;
280     }
281 
282     utils::Messanger messanger(ioc);
283     messanger.send(messages::UpdateReportInd{*reportIds});
284 }
285 } // namespace action
286