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