xref: /openbmc/phosphor-led-manager/fault-monitor/fru-fault-monitor.cpp (revision d1c1f0e990446c8862ae876302dd82a636d0968d)
1 #include "fru-fault-monitor.hpp"
2 
3 #include "elog-errors.hpp"
4 
5 #include <phosphor-logging/elog.hpp>
6 #include <phosphor-logging/lg2.hpp>
7 #include <sdbusplus/exception.hpp>
8 #include <xyz/openbmc_project/Led/Fru/Monitor/error.hpp>
9 #include <xyz/openbmc_project/Led/Mapper/error.hpp>
10 
11 namespace phosphor
12 {
13 namespace led
14 {
15 namespace fru
16 {
17 namespace fault
18 {
19 namespace monitor
20 {
21 
22 using namespace phosphor::logging;
23 
24 constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
25 constexpr auto MAPPER_OBJ_PATH = "/xyz/openbmc_project/object_mapper";
26 constexpr auto MAPPER_IFACE = "xyz.openbmc_project.ObjectMapper";
27 constexpr auto OBJMGR_IFACE = "org.freedesktop.DBus.ObjectManager";
28 constexpr auto LED_GROUPS = "/xyz/openbmc_project/led/groups/";
29 constexpr auto LOG_PATH = "/xyz/openbmc_project/logging";
30 constexpr auto LOG_IFACE = "xyz.openbmc_project.Logging.Entry";
31 
32 using AssociationList =
33     std::vector<std::tuple<std::string, std::string, std::string>>;
34 using Attributes = std::variant<bool, AssociationList>;
35 using PropertyName = std::string;
36 using PropertyMap = std::map<PropertyName, Attributes>;
37 using InterfaceName = std::string;
38 using InterfaceMap = std::map<InterfaceName, PropertyMap>;
39 
40 using Service = std::string;
41 using Path = std::string;
42 using Interface = std::string;
43 using Interfaces = std::vector<Interface>;
44 using MapperResponseType = std::map<Path, std::map<Service, Interfaces>>;
45 
46 using MethodErr =
47     sdbusplus::xyz::openbmc_project::Led::Mapper::Error::MethodError;
48 using ObjectNotFoundErr =
49     sdbusplus::xyz::openbmc_project::Led::Mapper::Error::ObjectNotFoundError;
50 using InventoryPathErr = sdbusplus::xyz::openbmc_project::Led::Fru::Monitor::
51     Error::InventoryPathError;
52 
53 std::string getService(sdbusplus::bus::bus& bus, const std::string& path)
54 {
55     auto mapper = bus.new_method_call(MAPPER_BUSNAME, MAPPER_OBJ_PATH,
56                                       MAPPER_IFACE, "GetObject");
57     mapper.append(path.c_str(), std::vector<std::string>({OBJMGR_IFACE}));
58 
59     std::map<std::string, std::vector<std::string>> mapperResponse;
60     try
61     {
62         auto mapperResponseMsg = bus.call(mapper);
63         mapperResponseMsg.read(mapperResponse);
64     }
65     catch (const sdbusplus::exception::exception& e)
66     {
67         lg2::error(
68             "Failed to parse getService mapper response, ERROR = {ERROR}",
69             "ERROR", e);
70         using namespace xyz::openbmc_project::Led::Mapper;
71         elog<ObjectNotFoundErr>(ObjectNotFoundError::METHOD_NAME("GetObject"),
72                                 ObjectNotFoundError::PATH(path.c_str()),
73                                 ObjectNotFoundError::INTERFACE(OBJMGR_IFACE));
74     }
75     if (mapperResponse.empty())
76     {
77         using namespace xyz::openbmc_project::Led::Mapper;
78         elog<ObjectNotFoundErr>(ObjectNotFoundError::METHOD_NAME("GetObject"),
79                                 ObjectNotFoundError::PATH(path.c_str()),
80                                 ObjectNotFoundError::INTERFACE(OBJMGR_IFACE));
81         return {};
82     }
83 
84     return mapperResponse.cbegin()->first;
85 }
86 
87 void action(sdbusplus::bus::bus& bus, const std::string& path, bool assert)
88 {
89     std::string service;
90     try
91     {
92         std::string groups{LED_GROUPS};
93         groups.pop_back();
94         service = getService(bus, groups);
95     }
96     catch (const MethodErr& e)
97     {
98         commit<MethodErr>();
99         return;
100     }
101     catch (const ObjectNotFoundErr& e)
102     {
103         commit<ObjectNotFoundErr>();
104         return;
105     }
106 
107     auto pos = path.rfind("/");
108     if (pos == std::string::npos)
109     {
110         using namespace xyz::openbmc_project::Led::Fru::Monitor;
111         report<InventoryPathErr>(InventoryPathError::PATH(path.c_str()));
112         return;
113     }
114     auto unit = path.substr(pos + 1);
115 
116     std::string ledPath = LED_GROUPS + unit + '_' + LED_FAULT;
117 
118     auto method = bus.new_method_call(service.c_str(), ledPath.c_str(),
119                                       "org.freedesktop.DBus.Properties", "Set");
120     method.append("xyz.openbmc_project.Led.Group");
121     method.append("Asserted");
122 
123     method.append(std::variant<bool>(assert));
124 
125     try
126     {
127         bus.call_noreply(method);
128     }
129     catch (const sdbusplus::exception::exception& e)
130     {
131         // Log an info message, system may not have all the LED Groups defined
132         lg2::info("Failed to Assert LED Group, ERROR = {ERROR}", "ERROR", e);
133     }
134 
135     return;
136 }
137 
138 void Add::created(sdbusplus::message::message& msg)
139 {
140     auto bus = msg.get_bus();
141 
142     sdbusplus::message::object_path objectPath;
143     InterfaceMap interfaces;
144     try
145     {
146         msg.read(objectPath, interfaces);
147     }
148     catch (const sdbusplus::exception::exception& e)
149     {
150         lg2::error("Failed to parse created message, ERROR = {ERROR}", "ERROR",
151                    e);
152         return;
153     }
154 
155     std::size_t found = objectPath.str.find(ELOG_ENTRY);
156     if (found == std::string::npos)
157     {
158         // Not a new error entry skip
159         return;
160     }
161     auto iter = interfaces.find("xyz.openbmc_project.Association.Definitions");
162     if (iter == interfaces.end())
163     {
164         return;
165     }
166 
167     // Nothing else shows when a specific error log
168     // has been created. Do it here.
169     // TODO:(phosphor-logging#25): support sdbusplus::message::object_path
170     // directly.
171     lg2::info("{PATH} created", "PATH", objectPath.str);
172 
173     auto attr = iter->second.find("Associations");
174     if (attr == iter->second.end())
175     {
176         return;
177     }
178 
179     auto& assocs = std::get<AssociationList>(attr->second);
180     if (assocs.empty())
181     {
182         // No associations skip
183         return;
184     }
185 
186     for (const auto& item : assocs)
187     {
188         if (std::get<1>(item).compare(CALLOUT_REV_ASSOCIATION) == 0)
189         {
190             removeWatches.emplace_back(
191                 std::make_unique<Remove>(bus, std::get<2>(item)));
192             action(bus, std::get<2>(item), true);
193         }
194     }
195 
196     return;
197 }
198 
199 void getLoggingSubTree(sdbusplus::bus::bus& bus, MapperResponseType& subtree)
200 {
201     auto depth = 0;
202     auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_OBJ_PATH,
203                                           MAPPER_IFACE, "GetSubTree");
204     mapperCall.append("/");
205     mapperCall.append(depth);
206     mapperCall.append(std::vector<Interface>({LOG_IFACE}));
207 
208     try
209     {
210         auto mapperResponseMsg = bus.call(mapperCall);
211         mapperResponseMsg.read(subtree);
212     }
213     catch (const sdbusplus::exception::exception& e)
214     {
215         lg2::error(
216             "Failed to parse existing callouts subtree message, ERROR = {ERROR}",
217             "ERROR", e);
218     }
219 }
220 
221 void Add::processExistingCallouts(sdbusplus::bus::bus& bus)
222 {
223     MapperResponseType mapperResponse;
224 
225     getLoggingSubTree(bus, mapperResponse);
226     if (mapperResponse.empty())
227     {
228         // No errors to process.
229         return;
230     }
231 
232     for (const auto& elem : mapperResponse)
233     {
234         auto method = bus.new_method_call(
235             elem.second.begin()->first.c_str(), elem.first.c_str(),
236             "org.freedesktop.DBus.Properties", "Get");
237         method.append("xyz.openbmc_project.Association.Definitions");
238         method.append("Associations");
239         auto reply = bus.call(method);
240         if (reply.is_method_error())
241         {
242             // do not stop, continue with next elog
243             lg2::error("Error in getting associations");
244             continue;
245         }
246 
247         std::variant<AssociationList> assoc;
248         try
249         {
250             reply.read(assoc);
251         }
252         catch (const sdbusplus::exception::exception& e)
253         {
254             lg2::error(
255                 "Failed to parse existing callouts associations message, ERROR = {ERROR}",
256                 "ERROR", e);
257             continue;
258         }
259         auto& assocs = std::get<AssociationList>(assoc);
260         if (assocs.empty())
261         {
262             // no associations, skip
263             continue;
264         }
265 
266         for (const auto& item : assocs)
267         {
268             if (std::get<1>(item).compare(CALLOUT_REV_ASSOCIATION) == 0)
269             {
270                 removeWatches.emplace_back(
271                     std::make_unique<Remove>(bus, std::get<2>(item)));
272                 action(bus, std::get<2>(item), true);
273             }
274         }
275     }
276 }
277 
278 void Remove::removed(sdbusplus::message::message& msg)
279 {
280     auto bus = msg.get_bus();
281 
282     action(bus, inventoryPath, false);
283     return;
284 }
285 
286 } // namespace monitor
287 } // namespace fault
288 } // namespace fru
289 } // namespace led
290 } // namespace phosphor
291