blob: 8f00ff4b3f954cccccad0ee784e87e2f61498ebb [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 Hansendaa4ac02025-08-27 15:42:08 +020079
80 // this represents the legacy quirk of upstream ports having no choice
81 // in the
82 // powered_by association
83 addPort(exposesType, path, assocPoweredBy);
Alexander Hansenec938ad2025-08-19 16:48:44 +020084 }
85 else
86 {
87 return;
88 }
89 boardTypes[path] = boardType;
90}
91
Alexander Hansen90fabb02025-08-27 14:15:17 +020092void Topology::addConfiguredPort(const Path& path,
93 const nlohmann::json& exposesItem)
94{
95 const auto findConnectsToName = exposesItem.find("Name");
96 if (findConnectsToName == exposesItem.end())
97 {
98 lg2::error("Board at path {PATH} is missing 'Name'", "PATH", path);
99 return;
100 }
101 const std::string connectsToName = findConnectsToName->get<std::string>();
102
103 const auto findPortType = exposesItem.find("PortType");
104 if (findPortType == exposesItem.end())
105 {
106 lg2::error("Board at path {PATH} is missing PortType", "PATH", path);
107 return;
108 }
109 const std::string portType = findPortType->get<std::string>();
110
111 const auto assoc = getAssocByName(portType);
112 if (!assoc.has_value())
113 {
114 lg2::error("Could not find configured association name {ASSOC}",
115 "ASSOC", portType);
116 return;
117 }
118
119 addPort(connectsToName, path, assoc.value());
120}
121
Alexander Hansenec938ad2025-08-19 16:48:44 +0200122void Topology::addDownstreamPort(const Path& path,
123 const nlohmann::json& exposesItem)
124{
125 auto findConnectsTo = exposesItem.find("ConnectsToType");
126 if (findConnectsTo == exposesItem.end())
127 {
Alexander Hansen315929a2025-08-19 17:34:10 +0200128 lg2::error("Board at path {PATH} is missing ConnectsToType", "PATH",
129 path);
Alexander Hansenec938ad2025-08-19 16:48:44 +0200130 return;
131 }
132 PortType connectsTo = findConnectsTo->get<std::string>();
133
Alexander Hansen7b2a77f2025-08-27 12:27:57 +0200134 addPort(connectsTo, path, assocContainedBy);
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200135
Alexander Hansenec938ad2025-08-19 16:48:44 +0200136 auto findPoweredBy = exposesItem.find("PowerPort");
137 if (findPoweredBy != exposesItem.end())
138 {
Alexander Hansen7b2a77f2025-08-27 12:27:57 +0200139 addPort(connectsTo, path, assocPowering);
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +0000140 }
141}
142
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200143void Topology::addPort(const PortType& port, const Path& path,
144 const AssocName& assocName)
145{
146 if (!ports.contains(port))
147 {
148 ports.insert({port, {}});
149 }
150 if (!ports[port].contains(path))
151 {
152 ports[port].insert({path, {}});
153 }
154 ports[port][path].insert(assocName);
155}
156
Alexander Hansen0519f572025-08-19 18:11:04 +0200157std::unordered_map<std::string, std::set<Association>> Topology::getAssocs(
Alexander Hansen32e74182025-08-20 16:16:00 +0200158 BoardPathsView boardPaths)
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +0000159{
Alexander Hansen0519f572025-08-19 18:11:04 +0200160 std::unordered_map<std::string, std::set<Association>> result;
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +0000161
162 // look at each upstream port type
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200163 for (const auto& port : ports)
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +0000164 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200165 fillAssocsForPortId(result, boardPaths, port.second);
Alexander Hansend7be0ee2025-08-20 14:16:15 +0200166 }
167 return result;
168}
169
170void Topology::fillAssocsForPortId(
171 std::unordered_map<std::string, std::set<Association>>& result,
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200172 BoardPathsView boardPaths,
173 const std::map<Path, std::set<AssocName>>& pathAssocs)
Alexander Hansend7be0ee2025-08-20 14:16:15 +0200174{
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200175 for (const auto& member : pathAssocs)
Alexander Hansend7be0ee2025-08-20 14:16:15 +0200176 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200177 for (const auto& other : pathAssocs)
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +0000178 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200179 if (other.first == member.first)
180 {
181 continue;
182 }
183 for (const auto& assocName : member.second)
184 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200185 // if the other end of the assocation does not declare
186 // the reverse association, do not associate
187 const bool otherAgrees =
Alexander Hansen7b2a77f2025-08-27 12:27:57 +0200188 other.second.contains(assocName.getReverse());
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200189
Alexander Hansendaa4ac02025-08-27 15:42:08 +0200190 if (!otherAgrees)
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200191 {
192 continue;
193 }
194
195 fillAssocForPortId(result, boardPaths, member.first,
196 other.first, assocName);
197 }
Alexander Hansen97a1c932025-08-20 16:28:52 +0200198 }
199 }
200}
201
202void Topology::fillAssocForPortId(
203 std::unordered_map<std::string, std::set<Association>>& result,
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200204 BoardPathsView boardPaths, const Path& upstream, const Path& downstream,
205 const AssocName& assocName)
Alexander Hansen97a1c932025-08-20 16:28:52 +0200206{
Alexander Hansen9b95ae42025-08-27 15:10:34 +0200207 if (!assocName.allowedOnBoardTypes.contains(boardTypes[upstream]))
Alexander Hansen97a1c932025-08-20 16:28:52 +0200208 {
Alexander Hansen9b95ae42025-08-27 15:10:34 +0200209 lg2::error(
210 "Cannot create Association Definition {ASSOC} for {PATH} with board type {TYPE}",
211 "ASSOC", assocName.name, "PATH", upstream, "TYPE",
212 boardTypes[upstream]);
Alexander Hansen5036d942025-08-20 16:38:59 +0200213 return;
214 }
215 // The downstream path must be one we care about.
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200216 if (!std::ranges::contains(boardPaths, upstream))
Alexander Hansen5036d942025-08-20 16:38:59 +0200217 {
218 return;
219 }
220
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200221 // quirk: legacy code did not associate from both sides
222 // TODO(alexander): revisit this
Alexander Hansen7b2a77f2025-08-27 12:27:57 +0200223 if (assocName == assocContaining || assocName == assocPoweredBy)
Alexander Hansen5036d942025-08-20 16:38:59 +0200224 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200225 return;
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +0000226 }
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200227
Alexander Hansen7b2a77f2025-08-27 12:27:57 +0200228 result[upstream].insert({assocName.name, assocName.reverse, downstream});
Alexander Hansenca121f52025-08-21 18:25:19 +0200229}
230
Matt Spinler6eb60972023-08-14 16:36:20 -0500231void Topology::remove(const std::string& boardName)
232{
233 // Remove the board from boardNames, and then using the path
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200234 // found in boardNames remove it from ports
Matt Spinler6eb60972023-08-14 16:36:20 -0500235 auto boardFind = boardNames.find(boardName);
236 if (boardFind == boardNames.end())
237 {
238 return;
239 }
240
241 std::string boardPath = boardFind->second;
242
243 boardNames.erase(boardFind);
244
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200245 for (auto& port : ports)
Matt Spinler6eb60972023-08-14 16:36:20 -0500246 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200247 port.second.erase(boardPath);
Matt Spinler6eb60972023-08-14 16:36:20 -0500248 }
249}