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