blob: c86e0e639a6ed864e73119915a031d7d225a9f48 [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
Alexander Hansen90fabb02025-08-27 14:15:17 +020018const std::vector<AssocName> supportedAssocs = {
19 assocContaining,
20 assocContainedBy,
21 assocPowering,
22 assocPoweredBy,
23};
24
Alexander Hansen9b95ae42025-08-27 15:10:34 +020025AssocName::AssocName(const std::string& name, const std::string& reverse,
26 const std::set<std::string>& allowedOnBoardTypes,
27 const std::set<std::string>& allowedOnBoardTypesReverse) :
28 name(name), reverse(reverse), allowedOnBoardTypes(allowedOnBoardTypes),
29 allowedOnBoardTypesReverse(allowedOnBoardTypesReverse)
Alexander Hansen7b2a77f2025-08-27 12:27:57 +020030{}
31
32AssocName AssocName::getReverse() const
33{
Alexander Hansen9b95ae42025-08-27 15:10:34 +020034 return {reverse, name, allowedOnBoardTypesReverse, allowedOnBoardTypes};
Alexander Hansen7b2a77f2025-08-27 12:27:57 +020035}
36
37bool AssocName::operator<(const AssocName& other) const
38{
39 return name < other.name;
40}
41
Alexander Hansen90fabb02025-08-27 14:15:17 +020042std::optional<AssocName> Topology::getAssocByName(const std::string& name)
43{
44 for (const auto& assoc : supportedAssocs)
45 {
46 if (assoc.name == name)
47 {
48 return assoc;
49 }
50 }
51 return std::nullopt;
52}
53
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000054void Topology::addBoard(const std::string& path, const std::string& boardType,
Matt Spinler6eb60972023-08-14 16:36:20 -050055 const std::string& boardName,
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000056 const nlohmann::json& exposesItem)
57{
58 auto findType = exposesItem.find("Type");
59 if (findType == exposesItem.end())
60 {
61 return;
62 }
Matt Spinler6eb60972023-08-14 16:36:20 -050063
64 boardNames.try_emplace(boardName, path);
65
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000066 PortType exposesType = findType->get<std::string>();
67
68 if (exposesType == "DownstreamPort")
69 {
Alexander Hansenec938ad2025-08-19 16:48:44 +020070 addDownstreamPort(path, exposesItem);
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000071 }
Alexander Hansen90fabb02025-08-27 14:15:17 +020072 else if (exposesType == "Port")
73 {
74 addConfiguredPort(path, exposesItem);
75 }
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000076 else if (exposesType.ends_with("Port"))
77 {
Alexander Hansen7b2a77f2025-08-27 12:27:57 +020078 addPort(exposesType, path, assocContaining);
Alexander Hansenec938ad2025-08-19 16:48:44 +020079 }
80 else
81 {
82 return;
83 }
84 boardTypes[path] = boardType;
85}
86
Alexander Hansen90fabb02025-08-27 14:15:17 +020087void Topology::addConfiguredPort(const Path& path,
88 const nlohmann::json& exposesItem)
89{
90 const auto findConnectsToName = exposesItem.find("Name");
91 if (findConnectsToName == exposesItem.end())
92 {
93 lg2::error("Board at path {PATH} is missing 'Name'", "PATH", path);
94 return;
95 }
96 const std::string connectsToName = findConnectsToName->get<std::string>();
97
98 const auto findPortType = exposesItem.find("PortType");
99 if (findPortType == exposesItem.end())
100 {
101 lg2::error("Board at path {PATH} is missing PortType", "PATH", path);
102 return;
103 }
104 const std::string portType = findPortType->get<std::string>();
105
106 const auto assoc = getAssocByName(portType);
107 if (!assoc.has_value())
108 {
109 lg2::error("Could not find configured association name {ASSOC}",
110 "ASSOC", portType);
111 return;
112 }
113
114 addPort(connectsToName, path, assoc.value());
115}
116
Alexander Hansenec938ad2025-08-19 16:48:44 +0200117void Topology::addDownstreamPort(const Path& path,
118 const nlohmann::json& exposesItem)
119{
120 auto findConnectsTo = exposesItem.find("ConnectsToType");
121 if (findConnectsTo == exposesItem.end())
122 {
Alexander Hansen315929a2025-08-19 17:34:10 +0200123 lg2::error("Board at path {PATH} is missing ConnectsToType", "PATH",
124 path);
Alexander Hansenec938ad2025-08-19 16:48:44 +0200125 return;
126 }
127 PortType connectsTo = findConnectsTo->get<std::string>();
128
Alexander Hansen7b2a77f2025-08-27 12:27:57 +0200129 addPort(connectsTo, path, assocContainedBy);
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200130
Alexander Hansenec938ad2025-08-19 16:48:44 +0200131 auto findPoweredBy = exposesItem.find("PowerPort");
132 if (findPoweredBy != exposesItem.end())
133 {
Alexander Hansen7b2a77f2025-08-27 12:27:57 +0200134 addPort(connectsTo, path, assocPowering);
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +0000135 }
136}
137
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200138void Topology::addPort(const PortType& port, const Path& path,
139 const AssocName& assocName)
140{
141 if (!ports.contains(port))
142 {
143 ports.insert({port, {}});
144 }
145 if (!ports[port].contains(path))
146 {
147 ports[port].insert({path, {}});
148 }
149 ports[port][path].insert(assocName);
150}
151
Alexander Hansen0519f572025-08-19 18:11:04 +0200152std::unordered_map<std::string, std::set<Association>> Topology::getAssocs(
Alexander Hansen32e74182025-08-20 16:16:00 +0200153 BoardPathsView boardPaths)
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +0000154{
Alexander Hansen0519f572025-08-19 18:11:04 +0200155 std::unordered_map<std::string, std::set<Association>> result;
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +0000156
157 // look at each upstream port type
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200158 for (const auto& port : ports)
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +0000159 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200160 fillAssocsForPortId(result, boardPaths, port.second);
Alexander Hansend7be0ee2025-08-20 14:16:15 +0200161 }
162 return result;
163}
164
165void Topology::fillAssocsForPortId(
166 std::unordered_map<std::string, std::set<Association>>& result,
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200167 BoardPathsView boardPaths,
168 const std::map<Path, std::set<AssocName>>& pathAssocs)
Alexander Hansend7be0ee2025-08-20 14:16:15 +0200169{
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200170 for (const auto& member : pathAssocs)
Alexander Hansend7be0ee2025-08-20 14:16:15 +0200171 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200172 for (const auto& other : pathAssocs)
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +0000173 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200174 if (other.first == member.first)
175 {
176 continue;
177 }
178 for (const auto& assocName : member.second)
179 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200180 // if the other end of the assocation does not declare
181 // the reverse association, do not associate
182 const bool otherAgrees =
Alexander Hansen7b2a77f2025-08-27 12:27:57 +0200183 other.second.contains(assocName.getReverse());
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200184
185 // quirk: since the other side of the association cannot declare
186 // to be powered_by in the legacy schema, in case of "powering",
187 // the two associations do not have to agree.
Alexander Hansen7b2a77f2025-08-27 12:27:57 +0200188 if (!otherAgrees && assocName != assocPowering)
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200189 {
190 continue;
191 }
192
193 fillAssocForPortId(result, boardPaths, member.first,
194 other.first, assocName);
195 }
Alexander Hansen97a1c932025-08-20 16:28:52 +0200196 }
197 }
198}
199
200void Topology::fillAssocForPortId(
201 std::unordered_map<std::string, std::set<Association>>& result,
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200202 BoardPathsView boardPaths, const Path& upstream, const Path& downstream,
203 const AssocName& assocName)
Alexander Hansen97a1c932025-08-20 16:28:52 +0200204{
Alexander Hansen9b95ae42025-08-27 15:10:34 +0200205 if (!assocName.allowedOnBoardTypes.contains(boardTypes[upstream]))
Alexander Hansen97a1c932025-08-20 16:28:52 +0200206 {
Alexander Hansen9b95ae42025-08-27 15:10:34 +0200207 lg2::error(
208 "Cannot create Association Definition {ASSOC} for {PATH} with board type {TYPE}",
209 "ASSOC", assocName.name, "PATH", upstream, "TYPE",
210 boardTypes[upstream]);
Alexander Hansen5036d942025-08-20 16:38:59 +0200211 return;
212 }
213 // The downstream path must be one we care about.
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200214 if (!std::ranges::contains(boardPaths, upstream))
Alexander Hansen5036d942025-08-20 16:38:59 +0200215 {
216 return;
217 }
218
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200219 // quirk: legacy code did not associate from both sides
220 // TODO(alexander): revisit this
Alexander Hansen7b2a77f2025-08-27 12:27:57 +0200221 if (assocName == assocContaining || assocName == assocPoweredBy)
Alexander Hansen5036d942025-08-20 16:38:59 +0200222 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200223 return;
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +0000224 }
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200225
Alexander Hansen7b2a77f2025-08-27 12:27:57 +0200226 result[upstream].insert({assocName.name, assocName.reverse, downstream});
Alexander Hansenca121f52025-08-21 18:25:19 +0200227}
228
Matt Spinler6eb60972023-08-14 16:36:20 -0500229void Topology::remove(const std::string& boardName)
230{
231 // Remove the board from boardNames, and then using the path
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200232 // found in boardNames remove it from ports
Matt Spinler6eb60972023-08-14 16:36:20 -0500233 auto boardFind = boardNames.find(boardName);
234 if (boardFind == boardNames.end())
235 {
236 return;
237 }
238
239 std::string boardPath = boardFind->second;
240
241 boardNames.erase(boardFind);
242
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200243 for (auto& port : ports)
Matt Spinler6eb60972023-08-14 16:36:20 -0500244 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200245 port.second.erase(boardPath);
Matt Spinler6eb60972023-08-14 16:36:20 -0500246 }
247}