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