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