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