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