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