xref: /openbmc/telemetry/src/trigger_factory.cpp (revision fdb06a14)
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), std::stod(thresholdValue), thresholdName,
166         severity));
167 }
168 
169 void TriggerFactory::makeNumericThreshold(
170     std::vector<std::shared_ptr<interfaces::Threshold>>& thresholds,
171     const std::vector<TriggerAction>& triggerActions,
172     const std::shared_ptr<std::vector<std::string>>& reportIds,
173     const Sensors& sensors,
174     const numeric::LabeledThresholdParam& thresholdParam) const
175 {
176     std::vector<std::unique_ptr<interfaces::TriggerAction>> actions;
177 
178     auto type = thresholdParam.at_label<ts::Type>();
179     auto dwellTime = Milliseconds(thresholdParam.at_label<ts::DwellTime>());
180     auto direction = thresholdParam.at_label<ts::Direction>();
181     auto thresholdValue = double{thresholdParam.at_label<ts::ThresholdValue>()};
182 
183     action::numeric::fillActions(actions, triggerActions, type, thresholdValue,
184                                  bus->get_io_context(), reportIds);
185 
186     thresholds.emplace_back(std::make_shared<NumericThreshold>(
187         bus->get_io_context(), sensors, std::move(actions), dwellTime,
188         direction, thresholdValue, type));
189 }
190 
191 void TriggerFactory::makeOnChangeThreshold(
192     std::vector<std::shared_ptr<interfaces::Threshold>>& thresholds,
193     const std::vector<TriggerAction>& triggerActions,
194     const std::shared_ptr<std::vector<std::string>>& reportIds,
195     const Sensors& sensors) const
196 {
197     std::vector<std::unique_ptr<interfaces::TriggerAction>> actions;
198 
199     action::discrete::onChange::fillActions(actions, triggerActions,
200                                             bus->get_io_context(), reportIds);
201 
202     thresholds.emplace_back(
203         std::make_shared<OnChangeThreshold>(sensors, std::move(actions)));
204 }
205 
206 std::unique_ptr<interfaces::Trigger> TriggerFactory::make(
207     const std::string& id, const std::string& name,
208     const std::vector<std::string>& triggerActionsIn,
209     const std::vector<std::string>& reportIdsIn,
210     interfaces::TriggerManager& triggerManager,
211     interfaces::JsonStorage& triggerStorage,
212     const LabeledTriggerThresholdParams& labeledThresholdParams,
213     const std::vector<LabeledSensorInfo>& labeledSensorsInfo) const
214 {
215     const auto& sensors = getSensors(labeledSensorsInfo);
216     auto triggerActions =
217         utils::transform(triggerActionsIn, [](const auto& triggerActionStr) {
218             return toTriggerAction(triggerActionStr);
219         });
220     std::vector<std::shared_ptr<interfaces::Threshold>> thresholds;
221 
222     auto reportIds = std::make_shared<std::vector<std::string>>(reportIdsIn);
223 
224     updateThresholds(thresholds, triggerActions, reportIds, sensors,
225                      labeledThresholdParams);
226 
227     return std::make_unique<Trigger>(
228         bus->get_io_context(), objServer, id, name, triggerActions, reportIds,
229         std::move(thresholds), triggerManager, triggerStorage, *this, sensors);
230 }
231 
232 Sensors TriggerFactory::getSensors(
233     const std::vector<LabeledSensorInfo>& labeledSensorsInfo) const
234 {
235     Sensors sensors;
236     updateSensors(sensors, labeledSensorsInfo);
237     return sensors;
238 }
239 
240 void TriggerFactory::updateSensors(
241     Sensors& currentSensors,
242     const std::vector<LabeledSensorInfo>& labeledSensorsInfo) const
243 {
244     Sensors oldSensors = currentSensors;
245     Sensors newSensors;
246 
247     for (const auto& labeledSensorInfo : labeledSensorsInfo)
248     {
249         auto existing = std::find_if(oldSensors.begin(), oldSensors.end(),
250                                      [labeledSensorInfo](auto sensor) {
251                                          return labeledSensorInfo ==
252                                                 sensor->getLabeledSensorInfo();
253                                      });
254 
255         if (existing != oldSensors.end())
256         {
257             newSensors.emplace_back(*existing);
258             oldSensors.erase(existing);
259             continue;
260         }
261 
262         const auto& service = labeledSensorInfo.at_label<ts::Service>();
263         const auto& sensorPath = labeledSensorInfo.at_label<ts::Path>();
264         const auto& metadata = labeledSensorInfo.at_label<ts::Metadata>();
265 
266         newSensors.emplace_back(sensorCache.makeSensor<Sensor>(
267             service, sensorPath, metadata, bus->get_io_context(), bus));
268     }
269 
270     currentSensors = std::move(newSensors);
271 }
272 
273 std::vector<LabeledSensorInfo>
274     TriggerFactory::getLabeledSensorsInfo(boost::asio::yield_context& yield,
275                                           const SensorsInfo& sensorsInfo) const
276 {
277     if (sensorsInfo.empty())
278     {
279         return {};
280     }
281     auto tree = utils::getSubTreeSensors(yield, bus);
282     return parseSensorTree(tree, sensorsInfo);
283 }
284 
285 std::vector<LabeledSensorInfo>
286     TriggerFactory::getLabeledSensorsInfo(const SensorsInfo& sensorsInfo) const
287 {
288     if (sensorsInfo.empty())
289     {
290         return {};
291     }
292     auto tree = utils::getSubTreeSensors(bus);
293     return parseSensorTree(tree, sensorsInfo);
294 }
295 
296 std::vector<LabeledSensorInfo>
297     TriggerFactory::parseSensorTree(const std::vector<utils::SensorTree>& tree,
298                                     const SensorsInfo& sensorsInfo)
299 {
300     return utils::transform(sensorsInfo, [&tree](const auto& item) {
301         const auto& [sensorPath, metadata] = item;
302         auto found = std::find_if(
303             tree.begin(), tree.end(),
304             [path = sensorPath](const auto& x) { return x.first == path; });
305 
306         if (tree.end() != found)
307         {
308             const auto& [service, ifaces] = found->second.front();
309             return LabeledSensorInfo(service, sensorPath, metadata);
310         }
311         throw std::runtime_error("Not found");
312     });
313 }
314