xref: /openbmc/telemetry/src/trigger_manager.cpp (revision 583ba441654657bb4ba9d051b747144a7258c159)
1 #include "trigger_manager.hpp"
2 
3 #include "trigger.hpp"
4 #include "types/trigger_types.hpp"
5 #include "utils/conversion_trigger.hpp"
6 #include "utils/dbus_path_utils.hpp"
7 #include "utils/make_id_name.hpp"
8 #include "utils/transform.hpp"
9 #include "utils/tstring.hpp"
10 
11 #include <phosphor-logging/log.hpp>
12 
13 #include <unordered_set>
14 
TriggerManager(std::unique_ptr<interfaces::TriggerFactory> triggerFactoryIn,std::unique_ptr<interfaces::JsonStorage> triggerStorageIn,const std::shared_ptr<sdbusplus::asio::object_server> & objServer)15 TriggerManager::TriggerManager(
16     std::unique_ptr<interfaces::TriggerFactory> triggerFactoryIn,
17     std::unique_ptr<interfaces::JsonStorage> triggerStorageIn,
18     const std::shared_ptr<sdbusplus::asio::object_server>& objServer) :
19     triggerFactory(std::move(triggerFactoryIn)),
20     triggerStorage(std::move(triggerStorageIn))
21 {
22     loadFromPersistent();
23 
24     managerIface = objServer->add_unique_interface(
25         triggerManagerPath, triggerManagerIfaceName, [this](auto& iface) {
26             iface.register_method(
27                 "AddTrigger",
28                 [this](
29                     boost::asio::yield_context& yield, const std::string& id,
30                     const std::string& name,
31                     const std::vector<std::string>& triggerActions,
32                     const SensorsInfo& sensors,
33                     const std::vector<sdbusplus::message::object_path>& reports,
34                     const std::vector<numeric::ThresholdParam>&
35                         numericThresholds,
36                     const std::vector<discrete::ThresholdParam>&
37                         discreteThresholds) {
38                     LabeledTriggerThresholdParams labeledTriggerThresholdParams;
39                     if (!numericThresholds.empty())
40                     {
41                         labeledTriggerThresholdParams =
42                             utils::ToLabeledThresholdParamConversion()(
43                                 numericThresholds);
44                     }
45                     if (!discreteThresholds.empty())
46                     {
47                         labeledTriggerThresholdParams =
48                             utils::ToLabeledThresholdParamConversion()(
49                                 discreteThresholds);
50                     }
51                     std::vector<LabeledSensorInfo> labeledSensorsInfo =
52                         triggerFactory->getLabeledSensorsInfo(yield, sensors);
53 
54                     auto reportIds = utils::transform<std::vector>(
55                         reports, [](const auto& item) {
56                             return utils::reportPathToId(item);
57                         });
58 
59                     return addTrigger(id, name, triggerActions,
60                                       labeledSensorsInfo, reportIds,
61                                       labeledTriggerThresholdParams)
62                         .getPath();
63                 });
64         });
65 }
66 
removeTrigger(const interfaces::Trigger * trigger)67 void TriggerManager::removeTrigger(const interfaces::Trigger* trigger)
68 {
69     triggers.erase(
70         std::remove_if(triggers.begin(), triggers.end(),
71                        [trigger](const auto& x) { return trigger == x.get(); }),
72         triggers.end());
73 }
74 
verifyReportIds(const std::vector<std::string> & newReportIds)75 void TriggerManager::verifyReportIds(
76     const std::vector<std::string>& newReportIds)
77 {
78     if (std::unordered_set(newReportIds.begin(), newReportIds.end()).size() !=
79         newReportIds.size())
80     {
81         throw sdbusplus::exception::SdBusError(
82             static_cast<int>(std::errc::invalid_argument),
83             "Duplicate element in ReportIds");
84     }
85 }
86 
verifyThresholdParams(const LabeledTriggerThresholdParams & thresholdParams)87 void TriggerManager::verifyThresholdParams(
88     const LabeledTriggerThresholdParams& thresholdParams)
89 {
90     namespace ts = utils::tstring;
91 
92     if (auto discreteParams =
93             std::get_if<std::vector<discrete::LabeledThresholdParam>>(
94                 &thresholdParams);
95         discreteParams != nullptr)
96     {
97         for (auto discreteParam : *discreteParams)
98         {
99             if (discreteParam.at_label<ts::UserId>().length() >
100                 utils::constants::maxIdNameLength)
101             {
102                 throw errors::InvalidArgument("ThresholdParams.Id",
103                                               "UserId too long.");
104             }
105         }
106     }
107 }
108 
verifyAddTrigger(const std::vector<std::string> & reportIds,const LabeledTriggerThresholdParams & thresholdParams) const109 void TriggerManager::verifyAddTrigger(
110     const std::vector<std::string>& reportIds,
111     const LabeledTriggerThresholdParams& thresholdParams) const
112 {
113     if (triggers.size() >= maxTriggers)
114     {
115         throw sdbusplus::exception::SdBusError(
116             static_cast<int>(std::errc::too_many_files_open),
117             "Reached maximal trigger count");
118     }
119 
120     verifyReportIds(reportIds);
121     verifyThresholdParams(thresholdParams);
122 }
123 
addTrigger(const std::string & triggerIdIn,const std::string & triggerNameIn,const std::vector<std::string> & triggerActions,const std::vector<LabeledSensorInfo> & labeledSensorsInfo,const std::vector<std::string> & reportIds,const LabeledTriggerThresholdParams & labeledThresholdParams)124 interfaces::Trigger& TriggerManager::addTrigger(
125     const std::string& triggerIdIn, const std::string& triggerNameIn,
126     const std::vector<std::string>& triggerActions,
127     const std::vector<LabeledSensorInfo>& labeledSensorsInfo,
128     const std::vector<std::string>& reportIds,
129     const LabeledTriggerThresholdParams& labeledThresholdParams)
130 {
131     const auto existingTriggerIds = utils::transform(
132         triggers, [](const auto& trigger) { return trigger->getId(); });
133 
134     auto [id, name] = utils::makeIdName(triggerIdIn, triggerNameIn,
135                                         triggerNameDefault, existingTriggerIds);
136 
137     verifyAddTrigger(reportIds, labeledThresholdParams);
138 
139     triggers.emplace_back(triggerFactory->make(
140         id, name, triggerActions, reportIds, *this, *triggerStorage,
141         labeledThresholdParams, labeledSensorsInfo));
142 
143     return *triggers.back();
144 }
145 
loadFromPersistent()146 void TriggerManager::loadFromPersistent()
147 {
148     std::vector<interfaces::JsonStorage::FilePath> paths =
149         triggerStorage->list();
150 
151     for (const auto& path : paths)
152     {
153         std::optional<nlohmann::json> data = triggerStorage->load(path);
154         try
155         {
156             if (!data.has_value())
157             {
158                 throw std::runtime_error("Empty storage");
159             }
160             size_t version = data->at("Version").get<size_t>();
161             if (version != Trigger::triggerVersion)
162             {
163                 throw std::runtime_error("Invalid version");
164             }
165             const std::string& id = data->at("Id").get_ref<std::string&>();
166             const std::string& name = data->at("Name").get_ref<std::string&>();
167             int thresholdParamsDiscriminator =
168                 data->at("ThresholdParamsDiscriminator").get<int>();
169             const std::vector<std::string> triggerActions =
170                 data->at("TriggerActions").get<std::vector<std::string>>();
171 
172             LabeledTriggerThresholdParams labeledThresholdParams;
173             if (0 == thresholdParamsDiscriminator)
174             {
175                 labeledThresholdParams =
176                     data->at("ThresholdParams")
177                         .get<std::vector<numeric::LabeledThresholdParam>>();
178             }
179             else
180             {
181                 labeledThresholdParams =
182                     data->at("ThresholdParams")
183                         .get<std::vector<discrete::LabeledThresholdParam>>();
184             }
185 
186             auto reportIds =
187                 data->at("ReportIds").get<std::vector<std::string>>();
188 
189             auto labeledSensorsInfo =
190                 data->at("Sensors").get<std::vector<LabeledSensorInfo>>();
191 
192             addTrigger(id, name, triggerActions, labeledSensorsInfo, reportIds,
193                        labeledThresholdParams);
194         }
195         catch (const std::exception& e)
196         {
197             phosphor::logging::log<phosphor::logging::level::ERR>(
198                 "Failed to load trigger from storage",
199                 phosphor::logging::entry(
200                     "FILENAME=%s",
201                     static_cast<std::filesystem::path>(path).c_str()),
202                 phosphor::logging::entry("EXCEPTION_MSG=%s", e.what()));
203             triggerStorage->remove(path);
204         }
205     }
206 }
207