blob: 16af9cd36694c05886990ffcd2159a8c16b7de8a [file] [log] [blame]
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +00001#include "topology.hpp"
2
Alexander Hansen315929a2025-08-19 17:34:10 +02003#include "phosphor-logging/lg2.hpp"
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +00004
5void Topology::addBoard(const std::string& path, const std::string& boardType,
Matt Spinler6eb60972023-08-14 16:36:20 -05006 const std::string& boardName,
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +00007 const nlohmann::json& exposesItem)
8{
9 auto findType = exposesItem.find("Type");
10 if (findType == exposesItem.end())
11 {
12 return;
13 }
Matt Spinler6eb60972023-08-14 16:36:20 -050014
15 boardNames.try_emplace(boardName, path);
16
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000017 PortType exposesType = findType->get<std::string>();
18
19 if (exposesType == "DownstreamPort")
20 {
Alexander Hansenec938ad2025-08-19 16:48:44 +020021 addDownstreamPort(path, exposesItem);
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000022 }
23 else if (exposesType.ends_with("Port"))
24 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +020025 addPort(exposesType, path, "containing");
Alexander Hansenec938ad2025-08-19 16:48:44 +020026 }
27 else
28 {
29 return;
30 }
31 boardTypes[path] = boardType;
32}
33
34void Topology::addDownstreamPort(const Path& path,
35 const nlohmann::json& exposesItem)
36{
37 auto findConnectsTo = exposesItem.find("ConnectsToType");
38 if (findConnectsTo == exposesItem.end())
39 {
Alexander Hansen315929a2025-08-19 17:34:10 +020040 lg2::error("Board at path {PATH} is missing ConnectsToType", "PATH",
41 path);
Alexander Hansenec938ad2025-08-19 16:48:44 +020042 return;
43 }
44 PortType connectsTo = findConnectsTo->get<std::string>();
45
Alexander Hansen68e2b0f2025-08-21 17:44:18 +020046 addPort(connectsTo, path, "contained_by");
47
Alexander Hansenec938ad2025-08-19 16:48:44 +020048 auto findPoweredBy = exposesItem.find("PowerPort");
49 if (findPoweredBy != exposesItem.end())
50 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +020051 addPort(connectsTo, path, "powering");
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000052 }
53}
54
Alexander Hansen68e2b0f2025-08-21 17:44:18 +020055void Topology::addPort(const PortType& port, const Path& path,
56 const AssocName& assocName)
57{
58 if (!ports.contains(port))
59 {
60 ports.insert({port, {}});
61 }
62 if (!ports[port].contains(path))
63 {
64 ports[port].insert({path, {}});
65 }
66 ports[port][path].insert(assocName);
67}
68
Alexander Hansen0519f572025-08-19 18:11:04 +020069std::unordered_map<std::string, std::set<Association>> Topology::getAssocs(
Alexander Hansen32e74182025-08-20 16:16:00 +020070 BoardPathsView boardPaths)
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000071{
Alexander Hansen0519f572025-08-19 18:11:04 +020072 std::unordered_map<std::string, std::set<Association>> result;
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000073
74 // look at each upstream port type
Alexander Hansen68e2b0f2025-08-21 17:44:18 +020075 for (const auto& port : ports)
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000076 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +020077 fillAssocsForPortId(result, boardPaths, port.second);
Alexander Hansend7be0ee2025-08-20 14:16:15 +020078 }
79 return result;
80}
81
82void Topology::fillAssocsForPortId(
83 std::unordered_map<std::string, std::set<Association>>& result,
Alexander Hansen68e2b0f2025-08-21 17:44:18 +020084 BoardPathsView boardPaths,
85 const std::map<Path, std::set<AssocName>>& pathAssocs)
Alexander Hansend7be0ee2025-08-20 14:16:15 +020086{
Alexander Hansen68e2b0f2025-08-21 17:44:18 +020087 for (const auto& member : pathAssocs)
Alexander Hansend7be0ee2025-08-20 14:16:15 +020088 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +020089 for (const auto& other : pathAssocs)
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000090 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +020091 if (other.first == member.first)
92 {
93 continue;
94 }
95 for (const auto& assocName : member.second)
96 {
97 auto optReverse = getOppositeAssoc(assocName);
98 if (!optReverse.has_value())
99 {
100 continue;
101 }
102 // if the other end of the assocation does not declare
103 // the reverse association, do not associate
104 const bool otherAgrees =
105 other.second.contains(optReverse.value());
106
107 // quirk: since the other side of the association cannot declare
108 // to be powered_by in the legacy schema, in case of "powering",
109 // the two associations do not have to agree.
110 if (!otherAgrees && assocName != "powering")
111 {
112 continue;
113 }
114
115 fillAssocForPortId(result, boardPaths, member.first,
116 other.first, assocName);
117 }
Alexander Hansen97a1c932025-08-20 16:28:52 +0200118 }
119 }
120}
121
122void Topology::fillAssocForPortId(
123 std::unordered_map<std::string, std::set<Association>>& result,
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200124 BoardPathsView boardPaths, const Path& upstream, const Path& downstream,
125 const AssocName& assocName)
Alexander Hansen97a1c932025-08-20 16:28:52 +0200126{
Alexander Hansen5036d942025-08-20 16:38:59 +0200127 if (boardTypes[upstream] != "Chassis" && boardTypes[upstream] != "Board")
Alexander Hansen97a1c932025-08-20 16:28:52 +0200128 {
Alexander Hansen5036d942025-08-20 16:38:59 +0200129 return;
130 }
131 // The downstream path must be one we care about.
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200132 if (!std::ranges::contains(boardPaths, upstream))
Alexander Hansen5036d942025-08-20 16:38:59 +0200133 {
134 return;
135 }
136
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200137 auto optReverse = getOppositeAssoc(assocName);
Alexander Hansenca121f52025-08-21 18:25:19 +0200138
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200139 if (!optReverse)
Alexander Hansenca121f52025-08-21 18:25:19 +0200140 {
141 return;
142 }
143
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200144 // quirk: legacy code did not associate from both sides
145 // TODO(alexander): revisit this
146 if (assocName == "containing" || assocName == "powered_by")
Alexander Hansen5036d942025-08-20 16:38:59 +0200147 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200148 return;
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +0000149 }
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200150
151 result[upstream].insert({assocName, optReverse.value(), downstream});
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +0000152}
Matt Spinler6eb60972023-08-14 16:36:20 -0500153
Alexander Hansenca121f52025-08-21 18:25:19 +0200154const std::set<std::pair<std::string, std::string>> assocs = {
155 {"powering", "powered_by"}, {"containing", "contained_by"},
156 // ... extend as needed
157};
158
159std::optional<std::string> Topology::getOppositeAssoc(
160 const AssocName& assocName)
161{
162 for (const auto& entry : assocs)
163 {
164 if (entry.first == assocName)
165 {
166 return entry.second;
167 }
168 if (entry.second == assocName)
169 {
170 return entry.first;
171 }
172 }
173
174 return std::nullopt;
175}
176
Matt Spinler6eb60972023-08-14 16:36:20 -0500177void Topology::remove(const std::string& boardName)
178{
179 // Remove the board from boardNames, and then using the path
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200180 // found in boardNames remove it from ports
Matt Spinler6eb60972023-08-14 16:36:20 -0500181 auto boardFind = boardNames.find(boardName);
182 if (boardFind == boardNames.end())
183 {
184 return;
185 }
186
187 std::string boardPath = boardFind->second;
188
189 boardNames.erase(boardFind);
190
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200191 for (auto& port : ports)
Matt Spinler6eb60972023-08-14 16:36:20 -0500192 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200193 port.second.erase(boardPath);
Matt Spinler6eb60972023-08-14 16:36:20 -0500194 }
195}