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