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