blob: f9aa6dc9e6c29ca665865fa757f5af0afd57ab1f [file] [log] [blame]
Andrew Geisslera80a3af2019-02-04 14:01:49 -06001#include "associations.hpp"
2
Andrew Geissler4511b332019-02-21 15:40:40 -06003#include <iostream>
4
Andrew Geisslera80a3af2019-02-04 14:01:49 -06005void removeAssociation(const std::string& sourcePath, const std::string& owner,
6 sdbusplus::asio::object_server& server,
Matt Spinlere2359fb2019-04-05 14:11:33 -05007 AssociationMaps& assocMaps)
Andrew Geisslera80a3af2019-02-04 14:01:49 -06008{
9 // Use associationOwners to find the association paths and endpoints
10 // that the passed in object path and service own. Remove all of
11 // these endpoints from the actual association D-Bus objects, and if
12 // the endpoints property is then empty, the whole association object
13 // can be removed. Note there can be multiple services that own an
14 // association, and also that sourcePath is the path of the object
15 // that contains the org.openbmc.Associations interface and not the
16 // association path itself.
17
18 // Find the services that have associations for this object path
Matt Spinlere2359fb2019-04-05 14:11:33 -050019 auto owners = assocMaps.owners.find(sourcePath);
20 if (owners == assocMaps.owners.end())
Andrew Geisslera80a3af2019-02-04 14:01:49 -060021 {
22 return;
23 }
24
25 // Find the association paths and endpoints owned by this object
26 // path for this service.
27 auto assocs = owners->second.find(owner);
28 if (assocs == owners->second.end())
29 {
30 return;
31 }
32
33 for (const auto& [assocPath, endpointsToRemove] : assocs->second)
34 {
Andrew Geissler5629ae82019-02-21 12:59:09 -060035 removeAssociationEndpoints(server, assocPath, endpointsToRemove,
Matt Spinlere2359fb2019-04-05 14:11:33 -050036 assocMaps);
Andrew Geisslera80a3af2019-02-04 14:01:49 -060037 }
38
39 // Remove the associationOwners entries for this owning path/service.
40 owners->second.erase(assocs);
41 if (owners->second.empty())
42 {
Matt Spinlere2359fb2019-04-05 14:11:33 -050043 assocMaps.owners.erase(owners);
Andrew Geisslera80a3af2019-02-04 14:01:49 -060044 }
Matt Spinlercb9bcdb2019-04-08 10:58:49 -050045
46 // If we were still waiting on the other side of this association to
47 // show up, cancel that wait.
48 removeFromPendingAssociations(sourcePath, assocMaps);
Andrew Geisslera80a3af2019-02-04 14:01:49 -060049}
Andrew Geisslerff5ce922019-02-21 12:43:09 -060050
51void removeAssociationEndpoints(
52 sdbusplus::asio::object_server& objectServer, const std::string& assocPath,
53 const boost::container::flat_set<std::string>& endpointsToRemove,
Matt Spinlere2359fb2019-04-05 14:11:33 -050054 AssociationMaps& assocMaps)
Andrew Geisslerff5ce922019-02-21 12:43:09 -060055{
Matt Spinlere2359fb2019-04-05 14:11:33 -050056 auto assoc = assocMaps.ifaces.find(assocPath);
57 if (assoc == assocMaps.ifaces.end())
Andrew Geisslerff5ce922019-02-21 12:43:09 -060058 {
59 return;
60 }
61
62 auto& endpointsInDBus = std::get<endpointsPos>(assoc->second);
63
64 for (const auto& endpointToRemove : endpointsToRemove)
65 {
66 auto e = std::find(endpointsInDBus.begin(), endpointsInDBus.end(),
67 endpointToRemove);
68
69 if (e != endpointsInDBus.end())
70 {
71 endpointsInDBus.erase(e);
72 }
73 }
74
75 if (endpointsInDBus.empty())
76 {
77 objectServer.remove_interface(std::get<ifacePos>(assoc->second));
78 std::get<ifacePos>(assoc->second) = nullptr;
79 std::get<endpointsPos>(assoc->second).clear();
80 }
81 else
82 {
83 std::get<ifacePos>(assoc->second)
84 ->set_property("endpoints", endpointsInDBus);
85 }
86}
Andrew Geissler7f1c44d2019-02-21 13:44:16 -060087
88void checkAssociationEndpointRemoves(
89 const std::string& sourcePath, const std::string& owner,
90 const AssociationPaths& newAssociations,
Matt Spinlere2359fb2019-04-05 14:11:33 -050091 sdbusplus::asio::object_server& objectServer, AssociationMaps& assocMaps)
Andrew Geissler7f1c44d2019-02-21 13:44:16 -060092{
93 // Find the services that have associations on this path.
Matt Spinlere2359fb2019-04-05 14:11:33 -050094 auto originalOwners = assocMaps.owners.find(sourcePath);
95 if (originalOwners == assocMaps.owners.end())
Andrew Geissler7f1c44d2019-02-21 13:44:16 -060096 {
97 return;
98 }
99
100 // Find the associations for this service
101 auto originalAssociations = originalOwners->second.find(owner);
102 if (originalAssociations == originalOwners->second.end())
103 {
104 return;
105 }
106
107 // Compare the new endpoints versus the original endpoints, and
108 // remove any of the original ones that aren't in the new list.
109 for (const auto& [originalAssocPath, originalEndpoints] :
110 originalAssociations->second)
111 {
112 // Check if this source even still has each association that
113 // was there previously, and if not, remove all of its endpoints
114 // from the D-Bus endpoints property which will cause the whole
115 // association path to be removed if no endpoints remain.
116 auto newEndpoints = newAssociations.find(originalAssocPath);
117 if (newEndpoints == newAssociations.end())
118 {
119 removeAssociationEndpoints(objectServer, originalAssocPath,
Matt Spinlere2359fb2019-04-05 14:11:33 -0500120 originalEndpoints, assocMaps);
Andrew Geissler7f1c44d2019-02-21 13:44:16 -0600121 }
122 else
123 {
124 // The association is still there. Check if the endpoints
125 // changed.
126 boost::container::flat_set<std::string> toRemove;
127
128 for (auto& originalEndpoint : originalEndpoints)
129 {
130 if (std::find(newEndpoints->second.begin(),
131 newEndpoints->second.end(),
132 originalEndpoint) == newEndpoints->second.end())
133 {
134 toRemove.emplace(originalEndpoint);
135 }
136 }
137 if (!toRemove.empty())
138 {
139 removeAssociationEndpoints(objectServer, originalAssocPath,
Matt Spinlere2359fb2019-04-05 14:11:33 -0500140 toRemove, assocMaps);
Andrew Geissler7f1c44d2019-02-21 13:44:16 -0600141 }
142 }
143 }
144}
Andrew Geissler4511b332019-02-21 15:40:40 -0600145
Matt Spinler11401e22019-04-08 13:13:25 -0500146void addEndpointsToAssocIfaces(
147 sdbusplus::asio::object_server& objectServer, const std::string& assocPath,
148 const boost::container::flat_set<std::string>& endpointPaths,
149 AssociationMaps& assocMaps)
150{
151 auto& iface = assocMaps.ifaces[assocPath];
152 auto& i = std::get<ifacePos>(iface);
153 auto& endpoints = std::get<endpointsPos>(iface);
154
155 // Only add new endpoints
156 for (auto& e : endpointPaths)
157 {
158 if (std::find(endpoints.begin(), endpoints.end(), e) == endpoints.end())
159 {
160 endpoints.push_back(e);
161 }
162 }
163
164 // If the interface already exists, only need to update
165 // the property value, otherwise create it
166 if (i)
167 {
168 i->set_property("endpoints", endpoints);
169 }
170 else
171 {
172 i = objectServer.add_interface(assocPath, XYZ_ASSOCIATION_INTERFACE);
173 i->register_property("endpoints", endpoints);
174 i->initialize();
175 }
176}
177
Andrew Geissler4511b332019-02-21 15:40:40 -0600178void associationChanged(sdbusplus::asio::object_server& objectServer,
179 const std::vector<Association>& associations,
180 const std::string& path, const std::string& owner,
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500181 const interface_map_type& interfaceMap,
Matt Spinlere2359fb2019-04-05 14:11:33 -0500182 AssociationMaps& assocMaps)
Andrew Geissler4511b332019-02-21 15:40:40 -0600183{
184 AssociationPaths objects;
185
186 for (const Association& association : associations)
187 {
188 std::string forward;
189 std::string reverse;
190 std::string endpoint;
191 std::tie(forward, reverse, endpoint) = association;
192
Andrew Geissler0a560a52019-03-22 10:59:07 -0500193 if (endpoint.empty())
194 {
195 std::cerr << "Found invalid association on path " << path << "\n";
196 continue;
197 }
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500198
199 // Can't create this association if the endpoint isn't on D-Bus.
200 if (interfaceMap.find(endpoint) == interfaceMap.end())
201 {
202 addPendingAssociation(endpoint, reverse, path, forward, owner,
203 assocMaps);
204 continue;
205 }
206
Andrew Geissler4511b332019-02-21 15:40:40 -0600207 if (forward.size())
208 {
209 objects[path + "/" + forward].emplace(endpoint);
210 }
211 if (reverse.size())
212 {
Andrew Geissler4511b332019-02-21 15:40:40 -0600213 objects[endpoint + "/" + reverse].emplace(path);
214 }
215 }
216 for (const auto& object : objects)
217 {
Matt Spinler11401e22019-04-08 13:13:25 -0500218 addEndpointsToAssocIfaces(objectServer, object.first, object.second,
219 assocMaps);
Andrew Geissler4511b332019-02-21 15:40:40 -0600220 }
221
222 // Check for endpoints being removed instead of added
223 checkAssociationEndpointRemoves(path, owner, objects, objectServer,
Matt Spinlere2359fb2019-04-05 14:11:33 -0500224 assocMaps);
Andrew Geissler4511b332019-02-21 15:40:40 -0600225
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500226 if (!objects.empty())
Andrew Geissler4511b332019-02-21 15:40:40 -0600227 {
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500228 // Update associationOwners with the latest info
229 auto a = assocMaps.owners.find(path);
230 if (a != assocMaps.owners.end())
Andrew Geissler4511b332019-02-21 15:40:40 -0600231 {
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500232 auto o = a->second.find(owner);
233 if (o != a->second.end())
234 {
235 o->second = std::move(objects);
236 }
237 else
238 {
239 a->second.emplace(owner, std::move(objects));
240 }
Andrew Geissler4511b332019-02-21 15:40:40 -0600241 }
242 else
243 {
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500244 boost::container::flat_map<std::string, AssociationPaths> owners;
245 owners.emplace(owner, std::move(objects));
246 assocMaps.owners.emplace(path, owners);
Andrew Geissler4511b332019-02-21 15:40:40 -0600247 }
248 }
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500249}
250
251void addPendingAssociation(const std::string& objectPath,
252 const std::string& type,
253 const std::string& endpointPath,
254 const std::string& endpointType,
255 const std::string& owner, AssociationMaps& assocMaps)
256{
257 Association assoc{type, endpointType, endpointPath};
258
259 auto p = assocMaps.pending.find(objectPath);
260 if (p == assocMaps.pending.end())
261 {
262 ExistingEndpoints ee;
263 ee.emplace_back(owner, std::move(assoc));
264 assocMaps.pending.emplace(objectPath, std::move(ee));
265 }
Andrew Geissler4511b332019-02-21 15:40:40 -0600266 else
267 {
Matt Spinlere0b0e3a2019-04-08 10:39:23 -0500268 // Already waiting on this path for another association,
269 // so just add this endpoint and owner.
270 auto& endpoints = p->second;
271 auto e =
272 std::find_if(endpoints.begin(), endpoints.end(),
273 [&assoc, &owner](const auto& endpoint) {
274 return (std::get<ownerPos>(endpoint) == owner) &&
275 (std::get<assocPos>(endpoint) == assoc);
276 });
277 if (e == endpoints.end())
278 {
279 endpoints.emplace_back(owner, std::move(assoc));
280 }
Andrew Geissler4511b332019-02-21 15:40:40 -0600281 }
282}
Matt Spinlercb9bcdb2019-04-08 10:58:49 -0500283
284void removeFromPendingAssociations(const std::string& endpointPath,
285 AssociationMaps& assocMaps)
286{
287 auto assoc = assocMaps.pending.begin();
288 while (assoc != assocMaps.pending.end())
289 {
290 auto endpoint = assoc->second.begin();
291 while (endpoint != assoc->second.end())
292 {
293 auto& e = std::get<assocPos>(*endpoint);
294 if (std::get<reversePathPos>(e) == endpointPath)
295 {
296 endpoint = assoc->second.erase(endpoint);
297 continue;
298 }
299
300 endpoint++;
301 }
302
303 if (assoc->second.empty())
304 {
305 assoc = assocMaps.pending.erase(assoc);
306 continue;
307 }
308
309 assoc++;
310 }
311}
Matt Spinler11401e22019-04-08 13:13:25 -0500312
313void addSingleAssociation(sdbusplus::asio::object_server& server,
314 const std::string& assocPath,
315 const std::string& endpoint, const std::string& owner,
316 const std::string& ownerPath,
317 AssociationMaps& assocMaps)
318{
319 boost::container::flat_set<std::string> endpoints{endpoint};
320
321 addEndpointsToAssocIfaces(server, assocPath, endpoints, assocMaps);
322
323 AssociationPaths objects;
324 boost::container::flat_set e{endpoint};
325 objects.emplace(assocPath, e);
326
327 auto a = assocMaps.owners.find(ownerPath);
328 if (a != assocMaps.owners.end())
329 {
330 auto o = a->second.find(owner);
331 if (o != a->second.end())
332 {
333 auto p = o->second.find(assocPath);
334 if (p != o->second.end())
335 {
336 p->second.emplace(endpoint);
337 }
338 else
339 {
340 o->second.emplace(assocPath, e);
341 }
342 }
343 else
344 {
345 a->second.emplace(owner, std::move(objects));
346 }
347 }
348 else
349 {
350 boost::container::flat_map<std::string, AssociationPaths> owners;
351 owners.emplace(owner, std::move(objects));
352 assocMaps.owners.emplace(endpoint, owners);
353 }
354}
355
356void checkIfPendingAssociation(const std::string& objectPath,
357 const interface_map_type& interfaceMap,
358 AssociationMaps& assocMaps,
359 sdbusplus::asio::object_server& server)
360{
361 auto pending = assocMaps.pending.find(objectPath);
362 if (pending == assocMaps.pending.end())
363 {
364 return;
365 }
366
367 if (interfaceMap.find(objectPath) == interfaceMap.end())
368 {
369 return;
370 }
371
372 auto endpoint = pending->second.begin();
373
374 while (endpoint != pending->second.end())
375 {
376 const auto& e = std::get<assocPos>(*endpoint);
377
378 // Ensure the other side of the association still exists
379 if (interfaceMap.find(std::get<reversePathPos>(e)) ==
380 interfaceMap.end())
381 {
382 endpoint++;
383 continue;
384 }
385
386 // Add both sides of the association:
387 // objectPath/forwardType and reversePath/reverseType
388 //
389 // The ownerPath is the reversePath - i.e. the endpoint that
390 // is on D-Bus and owns the org.openbmc.Associations iface.
391 //
392 const auto& ownerPath = std::get<reversePathPos>(e);
393 const auto& owner = std::get<ownerPos>(*endpoint);
394
395 auto assocPath = objectPath + '/' + std::get<forwardTypePos>(e);
396 auto endpointPath = ownerPath;
397
398 addSingleAssociation(server, assocPath, endpointPath, owner, ownerPath,
399 assocMaps);
400
401 // Now the reverse direction (still the same owner and ownerPath)
402 assocPath = endpointPath + '/' + std::get<reverseTypePos>(e);
403 endpointPath = objectPath;
404 addSingleAssociation(server, assocPath, endpointPath, owner, ownerPath,
405 assocMaps);
406
407 // Not pending anymore
408 endpoint = pending->second.erase(endpoint);
409 }
410
411 if (pending->second.empty())
412 {
413 assocMaps.pending.erase(objectPath);
414 }
415}