blob: bac865492e852a953521202e4e548aabc99179f0 [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 Hansen9b95ae42025-08-27 15:10:34 +02005const AssocName assocContaining =
6 AssocName("containing", "contained_by", {"Chassis"},
7 {"Board", "Chassis", "PowerSupply"});
Alexander Hansen7b2a77f2025-08-27 12:27:57 +02008const AssocName assocContainedBy = assocContaining.getReverse();
Alexander Hansen9b95ae42025-08-27 15:10:34 +02009
10// Topology tests say that a chassis can be powering another chassis.
11// In case there is any confusion as to why 'Chassis' can have 'powering'
12// association.
13const AssocName assocPowering =
14 AssocName("powering", "powered_by", {"Chassis", "PowerSupply"},
15 {"Board", "Chassis", "PowerSupply"});
Alexander Hansen7b2a77f2025-08-27 12:27:57 +020016const AssocName assocPoweredBy = assocPowering.getReverse();
17
Christopher Meis9a5eec92025-08-07 08:43:03 +020018const AssocName assocProbing = AssocName("probing", "probed_by", {}, {});
19const AssocName assocProbedBy = assocProbing.getReverse();
20
Alexander Hansen90fabb02025-08-27 14:15:17 +020021const std::vector<AssocName> supportedAssocs = {
22 assocContaining,
23 assocContainedBy,
24 assocPowering,
25 assocPoweredBy,
26};
27
Alexander Hansen9b95ae42025-08-27 15:10:34 +020028AssocName::AssocName(const std::string& name, const std::string& reverse,
29 const std::set<std::string>& allowedOnBoardTypes,
30 const std::set<std::string>& allowedOnBoardTypesReverse) :
31 name(name), reverse(reverse), allowedOnBoardTypes(allowedOnBoardTypes),
32 allowedOnBoardTypesReverse(allowedOnBoardTypesReverse)
Alexander Hansen7b2a77f2025-08-27 12:27:57 +020033{}
34
35AssocName AssocName::getReverse() const
36{
Alexander Hansen9b95ae42025-08-27 15:10:34 +020037 return {reverse, name, allowedOnBoardTypesReverse, allowedOnBoardTypes};
Alexander Hansen7b2a77f2025-08-27 12:27:57 +020038}
39
40bool AssocName::operator<(const AssocName& other) const
41{
42 return name < other.name;
43}
44
Alexander Hansen90fabb02025-08-27 14:15:17 +020045std::optional<AssocName> Topology::getAssocByName(const std::string& name)
46{
47 for (const auto& assoc : supportedAssocs)
48 {
49 if (assoc.name == name)
50 {
51 return assoc;
52 }
53 }
54 return std::nullopt;
55}
56
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000057void Topology::addBoard(const std::string& path, const std::string& boardType,
Matt Spinler6eb60972023-08-14 16:36:20 -050058 const std::string& boardName,
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000059 const nlohmann::json& exposesItem)
60{
61 auto findType = exposesItem.find("Type");
62 if (findType == exposesItem.end())
63 {
64 return;
65 }
Matt Spinler6eb60972023-08-14 16:36:20 -050066
67 boardNames.try_emplace(boardName, path);
68
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000069 PortType exposesType = findType->get<std::string>();
70
71 if (exposesType == "DownstreamPort")
72 {
Alexander Hansenec938ad2025-08-19 16:48:44 +020073 addDownstreamPort(path, exposesItem);
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000074 }
Alexander Hansen90fabb02025-08-27 14:15:17 +020075 else if (exposesType == "Port")
76 {
77 addConfiguredPort(path, exposesItem);
78 }
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000079 else if (exposesType.ends_with("Port"))
80 {
Alexander Hansen7b2a77f2025-08-27 12:27:57 +020081 addPort(exposesType, path, assocContaining);
Alexander Hansendaa4ac02025-08-27 15:42:08 +020082
83 // this represents the legacy quirk of upstream ports having no choice
84 // in the
85 // powered_by association
86 addPort(exposesType, path, assocPoweredBy);
Alexander Hansenec938ad2025-08-19 16:48:44 +020087 }
88 else
89 {
90 return;
91 }
92 boardTypes[path] = boardType;
93}
94
Alexander Hansen90fabb02025-08-27 14:15:17 +020095void Topology::addConfiguredPort(const Path& path,
96 const nlohmann::json& exposesItem)
97{
98 const auto findConnectsToName = exposesItem.find("Name");
99 if (findConnectsToName == exposesItem.end())
100 {
101 lg2::error("Board at path {PATH} is missing 'Name'", "PATH", path);
102 return;
103 }
104 const std::string connectsToName = findConnectsToName->get<std::string>();
105
106 const auto findPortType = exposesItem.find("PortType");
107 if (findPortType == exposesItem.end())
108 {
109 lg2::error("Board at path {PATH} is missing PortType", "PATH", path);
110 return;
111 }
112 const std::string portType = findPortType->get<std::string>();
113
114 const auto assoc = getAssocByName(portType);
115 if (!assoc.has_value())
116 {
117 lg2::error("Could not find configured association name {ASSOC}",
118 "ASSOC", portType);
119 return;
120 }
121
122 addPort(connectsToName, path, assoc.value());
123}
124
Alexander Hansenec938ad2025-08-19 16:48:44 +0200125void Topology::addDownstreamPort(const Path& path,
126 const nlohmann::json& exposesItem)
127{
128 auto findConnectsTo = exposesItem.find("ConnectsToType");
129 if (findConnectsTo == exposesItem.end())
130 {
Alexander Hansen315929a2025-08-19 17:34:10 +0200131 lg2::error("Board at path {PATH} is missing ConnectsToType", "PATH",
132 path);
Alexander Hansenec938ad2025-08-19 16:48:44 +0200133 return;
134 }
135 PortType connectsTo = findConnectsTo->get<std::string>();
136
Alexander Hansen7b2a77f2025-08-27 12:27:57 +0200137 addPort(connectsTo, path, assocContainedBy);
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200138
Alexander Hansenec938ad2025-08-19 16:48:44 +0200139 auto findPoweredBy = exposesItem.find("PowerPort");
140 if (findPoweredBy != exposesItem.end())
141 {
Alexander Hansen7b2a77f2025-08-27 12:27:57 +0200142 addPort(connectsTo, path, assocPowering);
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +0000143 }
144}
145
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200146void Topology::addPort(const PortType& port, const Path& path,
147 const AssocName& assocName)
148{
149 if (!ports.contains(port))
150 {
151 ports.insert({port, {}});
152 }
153 if (!ports[port].contains(path))
154 {
155 ports[port].insert({path, {}});
156 }
157 ports[port][path].insert(assocName);
158}
159
Alexander Hansen0519f572025-08-19 18:11:04 +0200160std::unordered_map<std::string, std::set<Association>> Topology::getAssocs(
Alexander Hansen32e74182025-08-20 16:16:00 +0200161 BoardPathsView boardPaths)
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +0000162{
Alexander Hansen0519f572025-08-19 18:11:04 +0200163 std::unordered_map<std::string, std::set<Association>> result;
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +0000164
165 // look at each upstream port type
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200166 for (const auto& port : ports)
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +0000167 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200168 fillAssocsForPortId(result, boardPaths, port.second);
Alexander Hansend7be0ee2025-08-20 14:16:15 +0200169 }
Christopher Meis9a5eec92025-08-07 08:43:03 +0200170
171 for (const auto& [boardPath, probePaths] : probePaths)
172 {
173 if (std::ranges::contains(boardPaths, boardPath))
174 {
175 for (const auto& path : probePaths)
176 {
177 result[boardPath].insert(
178 {assocProbing.name, assocProbedBy.name, path});
179 }
180 }
181 }
Alexander Hansend7be0ee2025-08-20 14:16:15 +0200182 return result;
183}
184
185void Topology::fillAssocsForPortId(
186 std::unordered_map<std::string, std::set<Association>>& result,
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200187 BoardPathsView boardPaths,
188 const std::map<Path, std::set<AssocName>>& pathAssocs)
Alexander Hansend7be0ee2025-08-20 14:16:15 +0200189{
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200190 for (const auto& member : pathAssocs)
Alexander Hansend7be0ee2025-08-20 14:16:15 +0200191 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200192 for (const auto& other : pathAssocs)
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +0000193 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200194 if (other.first == member.first)
195 {
196 continue;
197 }
198 for (const auto& assocName : member.second)
199 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200200 // if the other end of the assocation does not declare
201 // the reverse association, do not associate
202 const bool otherAgrees =
Alexander Hansen7b2a77f2025-08-27 12:27:57 +0200203 other.second.contains(assocName.getReverse());
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200204
Alexander Hansendaa4ac02025-08-27 15:42:08 +0200205 if (!otherAgrees)
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200206 {
207 continue;
208 }
209
210 fillAssocForPortId(result, boardPaths, member.first,
211 other.first, assocName);
212 }
Alexander Hansen97a1c932025-08-20 16:28:52 +0200213 }
214 }
215}
216
217void Topology::fillAssocForPortId(
218 std::unordered_map<std::string, std::set<Association>>& result,
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200219 BoardPathsView boardPaths, const Path& upstream, const Path& downstream,
220 const AssocName& assocName)
Alexander Hansen97a1c932025-08-20 16:28:52 +0200221{
Alexander Hansen9b95ae42025-08-27 15:10:34 +0200222 if (!assocName.allowedOnBoardTypes.contains(boardTypes[upstream]))
Alexander Hansen97a1c932025-08-20 16:28:52 +0200223 {
Alexander Hansen9b95ae42025-08-27 15:10:34 +0200224 lg2::error(
225 "Cannot create Association Definition {ASSOC} for {PATH} with board type {TYPE}",
226 "ASSOC", assocName.name, "PATH", upstream, "TYPE",
227 boardTypes[upstream]);
Alexander Hansen5036d942025-08-20 16:38:59 +0200228 return;
229 }
230 // The downstream path must be one we care about.
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200231 if (!std::ranges::contains(boardPaths, upstream))
Alexander Hansen5036d942025-08-20 16:38:59 +0200232 {
233 return;
234 }
235
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200236 // quirk: legacy code did not associate from both sides
237 // TODO(alexander): revisit this
Alexander Hansen7b2a77f2025-08-27 12:27:57 +0200238 if (assocName == assocContaining || assocName == assocPoweredBy)
Alexander Hansen5036d942025-08-20 16:38:59 +0200239 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200240 return;
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +0000241 }
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200242
Alexander Hansen7b2a77f2025-08-27 12:27:57 +0200243 result[upstream].insert({assocName.name, assocName.reverse, downstream});
Alexander Hansenca121f52025-08-21 18:25:19 +0200244}
245
Matt Spinler6eb60972023-08-14 16:36:20 -0500246void Topology::remove(const std::string& boardName)
247{
248 // Remove the board from boardNames, and then using the path
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200249 // found in boardNames remove it from ports
Matt Spinler6eb60972023-08-14 16:36:20 -0500250 auto boardFind = boardNames.find(boardName);
251 if (boardFind == boardNames.end())
252 {
253 return;
254 }
255
256 std::string boardPath = boardFind->second;
257
258 boardNames.erase(boardFind);
259
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200260 for (auto& port : ports)
Matt Spinler6eb60972023-08-14 16:36:20 -0500261 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200262 port.second.erase(boardPath);
Matt Spinler6eb60972023-08-14 16:36:20 -0500263 }
Christopher Meis9a5eec92025-08-07 08:43:03 +0200264
265 probePaths.erase(boardName);
266}
267
268void Topology::addProbePath(const std::string& boardPath,
269 const std::string& probePath)
270{
271 lg2::info("Probe path added: {PROBE} probed_by {BOARD} boards config",
272 "PROBE", probePath, "BOARD", boardPath);
273 probePaths[boardPath].emplace(probePath);
Matt Spinler6eb60972023-08-14 16:36:20 -0500274}