blob: 52ab47819779a8c56a07fc3e8054220e12bc9286 [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
Alexander Hansen7b2a77f2025-08-27 12:27:57 +02005const AssocName assocContaining = AssocName("containing", "contained_by");
6const AssocName assocContainedBy = assocContaining.getReverse();
7const AssocName assocPowering = AssocName("powering", "powered_by");
8const AssocName assocPoweredBy = assocPowering.getReverse();
9
Alexander Hansen90fabb02025-08-27 14:15:17 +020010const std::vector<AssocName> supportedAssocs = {
11 assocContaining,
12 assocContainedBy,
13 assocPowering,
14 assocPoweredBy,
15};
16
Alexander Hansen7b2a77f2025-08-27 12:27:57 +020017AssocName::AssocName(const std::string& name, const std::string& reverse) :
18 name(name), reverse(reverse)
19{}
20
21AssocName AssocName::getReverse() const
22{
23 return {reverse, name};
24}
25
26bool AssocName::operator<(const AssocName& other) const
27{
28 return name < other.name;
29}
30
Alexander Hansen90fabb02025-08-27 14:15:17 +020031std::optional<AssocName> Topology::getAssocByName(const std::string& name)
32{
33 for (const auto& assoc : supportedAssocs)
34 {
35 if (assoc.name == name)
36 {
37 return assoc;
38 }
39 }
40 return std::nullopt;
41}
42
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000043void Topology::addBoard(const std::string& path, const std::string& boardType,
Matt Spinler6eb60972023-08-14 16:36:20 -050044 const std::string& boardName,
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000045 const nlohmann::json& exposesItem)
46{
47 auto findType = exposesItem.find("Type");
48 if (findType == exposesItem.end())
49 {
50 return;
51 }
Matt Spinler6eb60972023-08-14 16:36:20 -050052
53 boardNames.try_emplace(boardName, path);
54
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000055 PortType exposesType = findType->get<std::string>();
56
57 if (exposesType == "DownstreamPort")
58 {
Alexander Hansenec938ad2025-08-19 16:48:44 +020059 addDownstreamPort(path, exposesItem);
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000060 }
Alexander Hansen90fabb02025-08-27 14:15:17 +020061 else if (exposesType == "Port")
62 {
63 addConfiguredPort(path, exposesItem);
64 }
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000065 else if (exposesType.ends_with("Port"))
66 {
Alexander Hansen7b2a77f2025-08-27 12:27:57 +020067 addPort(exposesType, path, assocContaining);
Alexander Hansenec938ad2025-08-19 16:48:44 +020068 }
69 else
70 {
71 return;
72 }
73 boardTypes[path] = boardType;
74}
75
Alexander Hansen90fabb02025-08-27 14:15:17 +020076void Topology::addConfiguredPort(const Path& path,
77 const nlohmann::json& exposesItem)
78{
79 const auto findConnectsToName = exposesItem.find("Name");
80 if (findConnectsToName == exposesItem.end())
81 {
82 lg2::error("Board at path {PATH} is missing 'Name'", "PATH", path);
83 return;
84 }
85 const std::string connectsToName = findConnectsToName->get<std::string>();
86
87 const auto findPortType = exposesItem.find("PortType");
88 if (findPortType == exposesItem.end())
89 {
90 lg2::error("Board at path {PATH} is missing PortType", "PATH", path);
91 return;
92 }
93 const std::string portType = findPortType->get<std::string>();
94
95 const auto assoc = getAssocByName(portType);
96 if (!assoc.has_value())
97 {
98 lg2::error("Could not find configured association name {ASSOC}",
99 "ASSOC", portType);
100 return;
101 }
102
103 addPort(connectsToName, path, assoc.value());
104}
105
Alexander Hansenec938ad2025-08-19 16:48:44 +0200106void Topology::addDownstreamPort(const Path& path,
107 const nlohmann::json& exposesItem)
108{
109 auto findConnectsTo = exposesItem.find("ConnectsToType");
110 if (findConnectsTo == exposesItem.end())
111 {
Alexander Hansen315929a2025-08-19 17:34:10 +0200112 lg2::error("Board at path {PATH} is missing ConnectsToType", "PATH",
113 path);
Alexander Hansenec938ad2025-08-19 16:48:44 +0200114 return;
115 }
116 PortType connectsTo = findConnectsTo->get<std::string>();
117
Alexander Hansen7b2a77f2025-08-27 12:27:57 +0200118 addPort(connectsTo, path, assocContainedBy);
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200119
Alexander Hansenec938ad2025-08-19 16:48:44 +0200120 auto findPoweredBy = exposesItem.find("PowerPort");
121 if (findPoweredBy != exposesItem.end())
122 {
Alexander Hansen7b2a77f2025-08-27 12:27:57 +0200123 addPort(connectsTo, path, assocPowering);
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +0000124 }
125}
126
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200127void Topology::addPort(const PortType& port, const Path& path,
128 const AssocName& assocName)
129{
130 if (!ports.contains(port))
131 {
132 ports.insert({port, {}});
133 }
134 if (!ports[port].contains(path))
135 {
136 ports[port].insert({path, {}});
137 }
138 ports[port][path].insert(assocName);
139}
140
Alexander Hansen0519f572025-08-19 18:11:04 +0200141std::unordered_map<std::string, std::set<Association>> Topology::getAssocs(
Alexander Hansen32e74182025-08-20 16:16:00 +0200142 BoardPathsView boardPaths)
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +0000143{
Alexander Hansen0519f572025-08-19 18:11:04 +0200144 std::unordered_map<std::string, std::set<Association>> result;
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +0000145
146 // look at each upstream port type
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200147 for (const auto& port : ports)
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +0000148 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200149 fillAssocsForPortId(result, boardPaths, port.second);
Alexander Hansend7be0ee2025-08-20 14:16:15 +0200150 }
151 return result;
152}
153
154void Topology::fillAssocsForPortId(
155 std::unordered_map<std::string, std::set<Association>>& result,
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200156 BoardPathsView boardPaths,
157 const std::map<Path, std::set<AssocName>>& pathAssocs)
Alexander Hansend7be0ee2025-08-20 14:16:15 +0200158{
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200159 for (const auto& member : pathAssocs)
Alexander Hansend7be0ee2025-08-20 14:16:15 +0200160 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200161 for (const auto& other : pathAssocs)
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +0000162 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200163 if (other.first == member.first)
164 {
165 continue;
166 }
167 for (const auto& assocName : member.second)
168 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200169 // if the other end of the assocation does not declare
170 // the reverse association, do not associate
171 const bool otherAgrees =
Alexander Hansen7b2a77f2025-08-27 12:27:57 +0200172 other.second.contains(assocName.getReverse());
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200173
174 // quirk: since the other side of the association cannot declare
175 // to be powered_by in the legacy schema, in case of "powering",
176 // the two associations do not have to agree.
Alexander Hansen7b2a77f2025-08-27 12:27:57 +0200177 if (!otherAgrees && assocName != assocPowering)
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200178 {
179 continue;
180 }
181
182 fillAssocForPortId(result, boardPaths, member.first,
183 other.first, assocName);
184 }
Alexander Hansen97a1c932025-08-20 16:28:52 +0200185 }
186 }
187}
188
189void Topology::fillAssocForPortId(
190 std::unordered_map<std::string, std::set<Association>>& result,
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200191 BoardPathsView boardPaths, const Path& upstream, const Path& downstream,
192 const AssocName& assocName)
Alexander Hansen97a1c932025-08-20 16:28:52 +0200193{
Alexander Hansen5036d942025-08-20 16:38:59 +0200194 if (boardTypes[upstream] != "Chassis" && boardTypes[upstream] != "Board")
Alexander Hansen97a1c932025-08-20 16:28:52 +0200195 {
Alexander Hansen5036d942025-08-20 16:38:59 +0200196 return;
197 }
198 // The downstream path must be one we care about.
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200199 if (!std::ranges::contains(boardPaths, upstream))
Alexander Hansen5036d942025-08-20 16:38:59 +0200200 {
201 return;
202 }
203
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200204 // quirk: legacy code did not associate from both sides
205 // TODO(alexander): revisit this
Alexander Hansen7b2a77f2025-08-27 12:27:57 +0200206 if (assocName == assocContaining || assocName == assocPoweredBy)
Alexander Hansen5036d942025-08-20 16:38:59 +0200207 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200208 return;
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +0000209 }
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200210
Alexander Hansen7b2a77f2025-08-27 12:27:57 +0200211 result[upstream].insert({assocName.name, assocName.reverse, downstream});
Alexander Hansenca121f52025-08-21 18:25:19 +0200212}
213
Matt Spinler6eb60972023-08-14 16:36:20 -0500214void Topology::remove(const std::string& boardName)
215{
216 // Remove the board from boardNames, and then using the path
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200217 // found in boardNames remove it from ports
Matt Spinler6eb60972023-08-14 16:36:20 -0500218 auto boardFind = boardNames.find(boardName);
219 if (boardFind == boardNames.end())
220 {
221 return;
222 }
223
224 std::string boardPath = boardFind->second;
225
226 boardNames.erase(boardFind);
227
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200228 for (auto& port : ports)
Matt Spinler6eb60972023-08-14 16:36:20 -0500229 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200230 port.second.erase(boardPath);
Matt Spinler6eb60972023-08-14 16:36:20 -0500231 }
232}