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