xref: /openbmc/telemetry/src/trigger_factory.cpp (revision 973b4bb0)
1 #include "trigger_factory.hpp"
2 
3 #include "discrete_threshold.hpp"
4 #include "numeric_threshold.hpp"
5 #include "on_change_threshold.hpp"
6 #include "sensor.hpp"
7 #include "trigger.hpp"
8 #include "trigger_actions.hpp"
9 #include "utils/clock.hpp"
10 #include "utils/dbus_mapper.hpp"
11 #include "utils/transform.hpp"
12 
13 namespace ts = utils::tstring;
14 
15 TriggerFactory::TriggerFactory(
16     std::shared_ptr<sdbusplus::asio::connection> bus,
17     std::shared_ptr<sdbusplus::asio::object_server> objServer,
18     SensorCache& sensorCache) :
19     bus(std::move(bus)),
20     objServer(std::move(objServer)), sensorCache(sensorCache)
21 {}
22 
23 void TriggerFactory::updateDiscreteThresholds(
24     std::vector<std::shared_ptr<interfaces::Threshold>>& currentThresholds,
25     const std::string& triggerId,
26     const std::vector<TriggerAction>& triggerActions,
27     const std::shared_ptr<std::vector<std::string>>& reportIds,
28     const Sensors& sensors,
29     const std::vector<discrete::LabeledThresholdParam>& newParams) const
30 {
31     auto oldThresholds = currentThresholds;
32     std::vector<std::shared_ptr<interfaces::Threshold>> newThresholds;
33 
34     bool isCurrentOnChange = false;
35     if (oldThresholds.size() == 1 &&
36         std::holds_alternative<std::monostate>(
37             oldThresholds.back()->getThresholdParam()))
38     {
39         isCurrentOnChange = true;
40     }
41 
42     newThresholds.reserve(newParams.size());
43 
44     if (!isCurrentOnChange)
45     {
46         for (const auto& labeledThresholdParam : newParams)
47         {
48             auto paramChecker = [labeledThresholdParam](auto threshold) {
49                 return labeledThresholdParam ==
50                        std::get<discrete::LabeledThresholdParam>(
51                            threshold->getThresholdParam());
52             };
53             if (auto existing = std::find_if(oldThresholds.begin(),
54                                              oldThresholds.end(), paramChecker);
55                 existing != oldThresholds.end())
56             {
57                 newThresholds.emplace_back(*existing);
58                 oldThresholds.erase(existing);
59                 continue;
60             }
61 
62             makeDiscreteThreshold(newThresholds, triggerId, triggerActions,
63                                   reportIds, sensors, labeledThresholdParam);
64         }
65     }
66     else
67     {
68         for (const auto& labeledThresholdParam : newParams)
69         {
70             makeDiscreteThreshold(newThresholds, triggerId, triggerActions,
71                                   reportIds, sensors, labeledThresholdParam);
72         }
73     }
74     if (newParams.empty())
75     {
76         if (isCurrentOnChange)
77         {
78             newThresholds.emplace_back(*oldThresholds.begin());
79         }
80         else
81         {
82             makeOnChangeThreshold(newThresholds, triggerId, triggerActions,
83                                   reportIds, sensors);
84         }
85     }
86     currentThresholds = std::move(newThresholds);
87 }
88 
89 void TriggerFactory::updateNumericThresholds(
90     std::vector<std::shared_ptr<interfaces::Threshold>>& currentThresholds,
91     const std::string& triggerId,
92     const std::vector<TriggerAction>& triggerActions,
93     const std::shared_ptr<std::vector<std::string>>& reportIds,
94     const Sensors& sensors,
95     const std::vector<numeric::LabeledThresholdParam>& newParams) const
96 {
97     auto oldThresholds = currentThresholds;
98     std::vector<std::shared_ptr<interfaces::Threshold>> newThresholds;
99 
100     newThresholds.reserve(newParams.size());
101 
102     for (const auto& labeledThresholdParam : newParams)
103     {
104         auto paramChecker = [labeledThresholdParam](auto threshold) {
105             return labeledThresholdParam ==
106                    std::get<numeric::LabeledThresholdParam>(
107                        threshold->getThresholdParam());
108         };
109         if (auto existing = std::find_if(oldThresholds.begin(),
110                                          oldThresholds.end(), paramChecker);
111             existing != oldThresholds.end())
112         {
113             newThresholds.emplace_back(*existing);
114             oldThresholds.erase(existing);
115             continue;
116         }
117 
118         makeNumericThreshold(newThresholds, triggerId, triggerActions,
119                              reportIds, sensors, labeledThresholdParam);
120     }
121     currentThresholds = std::move(newThresholds);
122 }
123 
124 void TriggerFactory::updateThresholds(
125     std::vector<std::shared_ptr<interfaces::Threshold>>& currentThresholds,
126     const std::string& triggerId,
127     const std::vector<TriggerAction>& triggerActions,
128     const std::shared_ptr<std::vector<std::string>>& reportIds,
129     const Sensors& sensors,
130     const LabeledTriggerThresholdParams& newParams) const
131 {
132     if (isTriggerThresholdDiscrete(newParams))
133     {
134         const auto& labeledDiscreteThresholdParams =
135             std::get<std::vector<discrete::LabeledThresholdParam>>(newParams);
136 
137         updateDiscreteThresholds(currentThresholds, triggerId, triggerActions,
138                                  reportIds, sensors,
139                                  labeledDiscreteThresholdParams);
140     }
141     else
142     {
143         const auto& labeledNumericThresholdParams =
144             std::get<std::vector<numeric::LabeledThresholdParam>>(newParams);
145 
146         updateNumericThresholds(currentThresholds, triggerId, triggerActions,
147                                 reportIds, sensors,
148                                 labeledNumericThresholdParams);
149     }
150 }
151 
152 void TriggerFactory::makeDiscreteThreshold(
153     std::vector<std::shared_ptr<interfaces::Threshold>>& thresholds,
154     const std::string& triggerId,
155     const std::vector<TriggerAction>& triggerActions,
156     const std::shared_ptr<std::vector<std::string>>& reportIds,
157     const Sensors& sensors,
158     const discrete::LabeledThresholdParam& thresholdParam) const
159 {
160     std::vector<std::unique_ptr<interfaces::TriggerAction>> actions;
161 
162     const std::string& thresholdName = thresholdParam.at_label<ts::UserId>();
163     discrete::Severity severity = thresholdParam.at_label<ts::Severity>();
164     auto dwellTime = Milliseconds(thresholdParam.at_label<ts::DwellTime>());
165     const std::string& thresholdValue =
166         thresholdParam.at_label<ts::ThresholdValue>();
167 
168     action::discrete::fillActions(actions, triggerActions, severity,
169                                   bus->get_io_context(), reportIds);
170 
171     thresholds.emplace_back(std::make_shared<DiscreteThreshold>(
172         bus->get_io_context(), triggerId, sensors, std::move(actions),
173         Milliseconds(dwellTime), thresholdValue, thresholdName, severity,
174         std::make_unique<Clock>()));
175 }
176 
177 void TriggerFactory::makeNumericThreshold(
178     std::vector<std::shared_ptr<interfaces::Threshold>>& thresholds,
179     const std::string& triggerId,
180     const std::vector<TriggerAction>& triggerActions,
181     const std::shared_ptr<std::vector<std::string>>& reportIds,
182     const Sensors& sensors,
183     const numeric::LabeledThresholdParam& thresholdParam) const
184 {
185     std::vector<std::unique_ptr<interfaces::TriggerAction>> actions;
186 
187     auto type = thresholdParam.at_label<ts::Type>();
188     auto dwellTime = Milliseconds(thresholdParam.at_label<ts::DwellTime>());
189     auto direction = thresholdParam.at_label<ts::Direction>();
190     auto thresholdValue = double{thresholdParam.at_label<ts::ThresholdValue>()};
191 
192     action::numeric::fillActions(actions, triggerActions, type, thresholdValue,
193                                  bus->get_io_context(), reportIds);
194 
195     thresholds.emplace_back(std::make_shared<NumericThreshold>(
196         bus->get_io_context(), triggerId, sensors, std::move(actions),
197         dwellTime, direction, thresholdValue, type, std::make_unique<Clock>()));
198 }
199 
200 void TriggerFactory::makeOnChangeThreshold(
201     std::vector<std::shared_ptr<interfaces::Threshold>>& thresholds,
202     const std::string& triggerId,
203     const std::vector<TriggerAction>& triggerActions,
204     const std::shared_ptr<std::vector<std::string>>& reportIds,
205     const Sensors& sensors) const
206 {
207     std::vector<std::unique_ptr<interfaces::TriggerAction>> actions;
208 
209     action::discrete::onChange::fillActions(actions, triggerActions,
210                                             bus->get_io_context(), reportIds);
211 
212     thresholds.emplace_back(std::make_shared<OnChangeThreshold>(
213         triggerId, sensors, std::move(actions), std::make_unique<Clock>()));
214 }
215 
216 std::unique_ptr<interfaces::Trigger> TriggerFactory::make(
217     const std::string& idIn, const std::string& name,
218     const std::vector<std::string>& triggerActionsIn,
219     const std::vector<std::string>& reportIdsIn,
220     interfaces::TriggerManager& triggerManager,
221     interfaces::JsonStorage& triggerStorage,
222     const LabeledTriggerThresholdParams& labeledThresholdParams,
223     const std::vector<LabeledSensorInfo>& labeledSensorsInfo) const
224 {
225     const auto& sensors = getSensors(labeledSensorsInfo);
226     auto triggerActions =
227         utils::transform(triggerActionsIn, [](const auto& triggerActionStr) {
228             return toTriggerAction(triggerActionStr);
229         });
230     std::vector<std::shared_ptr<interfaces::Threshold>> thresholds;
231     auto id = std::make_unique<const std::string>(idIn);
232 
233     auto reportIds = std::make_shared<std::vector<std::string>>(reportIdsIn);
234 
235     updateThresholds(thresholds, *id, triggerActions, reportIds, sensors,
236                      labeledThresholdParams);
237 
238     return std::make_unique<Trigger>(
239         bus->get_io_context(), objServer, std::move(id), name, triggerActions,
240         reportIds, std::move(thresholds), triggerManager, triggerStorage, *this,
241         sensors);
242 }
243 
244 Sensors TriggerFactory::getSensors(
245     const std::vector<LabeledSensorInfo>& labeledSensorsInfo) const
246 {
247     Sensors sensors;
248     updateSensors(sensors, labeledSensorsInfo);
249     return sensors;
250 }
251 
252 void TriggerFactory::updateSensors(
253     Sensors& currentSensors,
254     const std::vector<LabeledSensorInfo>& labeledSensorsInfo) const
255 {
256     Sensors oldSensors = currentSensors;
257     Sensors newSensors;
258 
259     for (const auto& labeledSensorInfo : labeledSensorsInfo)
260     {
261         auto existing = std::find_if(oldSensors.begin(), oldSensors.end(),
262                                      [labeledSensorInfo](auto sensor) {
263                                          return labeledSensorInfo ==
264                                                 sensor->getLabeledSensorInfo();
265                                      });
266 
267         if (existing != oldSensors.end())
268         {
269             newSensors.emplace_back(*existing);
270             oldSensors.erase(existing);
271             continue;
272         }
273 
274         const auto& service = labeledSensorInfo.at_label<ts::Service>();
275         const auto& sensorPath = labeledSensorInfo.at_label<ts::Path>();
276         const auto& metadata = labeledSensorInfo.at_label<ts::Metadata>();
277 
278         newSensors.emplace_back(sensorCache.makeSensor<Sensor>(
279             service, sensorPath, metadata, bus->get_io_context(), bus));
280     }
281 
282     currentSensors = std::move(newSensors);
283 }
284 
285 std::vector<LabeledSensorInfo>
286     TriggerFactory::getLabeledSensorsInfo(boost::asio::yield_context& yield,
287                                           const SensorsInfo& sensorsInfo) const
288 {
289     if (sensorsInfo.empty())
290     {
291         return {};
292     }
293     auto tree = utils::getSubTreeSensors(yield, bus);
294     return parseSensorTree(tree, sensorsInfo);
295 }
296 
297 std::vector<LabeledSensorInfo>
298     TriggerFactory::getLabeledSensorsInfo(const SensorsInfo& sensorsInfo) const
299 {
300     if (sensorsInfo.empty())
301     {
302         return {};
303     }
304     auto tree = utils::getSubTreeSensors(bus);
305     return parseSensorTree(tree, sensorsInfo);
306 }
307 
308 std::vector<LabeledSensorInfo>
309     TriggerFactory::parseSensorTree(const std::vector<utils::SensorTree>& tree,
310                                     const SensorsInfo& sensorsInfo)
311 {
312     return utils::transform(sensorsInfo, [&tree](const auto& item) {
313         const auto& [sensorPath, metadata] = item;
314         auto found = std::find_if(
315             tree.begin(), tree.end(),
316             [path = sensorPath](const auto& x) { return x.first == path; });
317 
318         if (tree.end() != found)
319         {
320             const auto& [service, ifaces] = found->second.front();
321             return LabeledSensorInfo(service, sensorPath, metadata);
322         }
323         throw std::runtime_error("Not found");
324     });
325 }
326