blob: 05819918a84c99261c78abfcd3298e3a498e34fa [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
10AssocName::AssocName(const std::string& name, const std::string& reverse) :
11 name(name), reverse(reverse)
12{}
13
14AssocName AssocName::getReverse() const
15{
16 return {reverse, name};
17}
18
19bool AssocName::operator<(const AssocName& other) const
20{
21 return name < other.name;
22}
23
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000024void Topology::addBoard(const std::string& path, const std::string& boardType,
Matt Spinler6eb60972023-08-14 16:36:20 -050025 const std::string& boardName,
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000026 const nlohmann::json& exposesItem)
27{
28 auto findType = exposesItem.find("Type");
29 if (findType == exposesItem.end())
30 {
31 return;
32 }
Matt Spinler6eb60972023-08-14 16:36:20 -050033
34 boardNames.try_emplace(boardName, path);
35
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000036 PortType exposesType = findType->get<std::string>();
37
38 if (exposesType == "DownstreamPort")
39 {
Alexander Hansenec938ad2025-08-19 16:48:44 +020040 addDownstreamPort(path, exposesItem);
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000041 }
42 else if (exposesType.ends_with("Port"))
43 {
Alexander Hansen7b2a77f2025-08-27 12:27:57 +020044 addPort(exposesType, path, assocContaining);
Alexander Hansenec938ad2025-08-19 16:48:44 +020045 }
46 else
47 {
48 return;
49 }
50 boardTypes[path] = boardType;
51}
52
53void Topology::addDownstreamPort(const Path& path,
54 const nlohmann::json& exposesItem)
55{
56 auto findConnectsTo = exposesItem.find("ConnectsToType");
57 if (findConnectsTo == exposesItem.end())
58 {
Alexander Hansen315929a2025-08-19 17:34:10 +020059 lg2::error("Board at path {PATH} is missing ConnectsToType", "PATH",
60 path);
Alexander Hansenec938ad2025-08-19 16:48:44 +020061 return;
62 }
63 PortType connectsTo = findConnectsTo->get<std::string>();
64
Alexander Hansen7b2a77f2025-08-27 12:27:57 +020065 addPort(connectsTo, path, assocContainedBy);
Alexander Hansen68e2b0f2025-08-21 17:44:18 +020066
Alexander Hansenec938ad2025-08-19 16:48:44 +020067 auto findPoweredBy = exposesItem.find("PowerPort");
68 if (findPoweredBy != exposesItem.end())
69 {
Alexander Hansen7b2a77f2025-08-27 12:27:57 +020070 addPort(connectsTo, path, assocPowering);
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000071 }
72}
73
Alexander Hansen68e2b0f2025-08-21 17:44:18 +020074void Topology::addPort(const PortType& port, const Path& path,
75 const AssocName& assocName)
76{
77 if (!ports.contains(port))
78 {
79 ports.insert({port, {}});
80 }
81 if (!ports[port].contains(path))
82 {
83 ports[port].insert({path, {}});
84 }
85 ports[port][path].insert(assocName);
86}
87
Alexander Hansen0519f572025-08-19 18:11:04 +020088std::unordered_map<std::string, std::set<Association>> Topology::getAssocs(
Alexander Hansen32e74182025-08-20 16:16:00 +020089 BoardPathsView boardPaths)
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000090{
Alexander Hansen0519f572025-08-19 18:11:04 +020091 std::unordered_map<std::string, std::set<Association>> result;
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000092
93 // look at each upstream port type
Alexander Hansen68e2b0f2025-08-21 17:44:18 +020094 for (const auto& port : ports)
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +000095 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +020096 fillAssocsForPortId(result, boardPaths, port.second);
Alexander Hansend7be0ee2025-08-20 14:16:15 +020097 }
98 return result;
99}
100
101void Topology::fillAssocsForPortId(
102 std::unordered_map<std::string, std::set<Association>>& result,
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200103 BoardPathsView boardPaths,
104 const std::map<Path, std::set<AssocName>>& pathAssocs)
Alexander Hansend7be0ee2025-08-20 14:16:15 +0200105{
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200106 for (const auto& member : pathAssocs)
Alexander Hansend7be0ee2025-08-20 14:16:15 +0200107 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200108 for (const auto& other : pathAssocs)
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +0000109 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200110 if (other.first == member.first)
111 {
112 continue;
113 }
114 for (const auto& assocName : member.second)
115 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200116 // if the other end of the assocation does not declare
117 // the reverse association, do not associate
118 const bool otherAgrees =
Alexander Hansen7b2a77f2025-08-27 12:27:57 +0200119 other.second.contains(assocName.getReverse());
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200120
121 // quirk: since the other side of the association cannot declare
122 // to be powered_by in the legacy schema, in case of "powering",
123 // the two associations do not have to agree.
Alexander Hansen7b2a77f2025-08-27 12:27:57 +0200124 if (!otherAgrees && assocName != assocPowering)
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200125 {
126 continue;
127 }
128
129 fillAssocForPortId(result, boardPaths, member.first,
130 other.first, assocName);
131 }
Alexander Hansen97a1c932025-08-20 16:28:52 +0200132 }
133 }
134}
135
136void Topology::fillAssocForPortId(
137 std::unordered_map<std::string, std::set<Association>>& result,
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200138 BoardPathsView boardPaths, const Path& upstream, const Path& downstream,
139 const AssocName& assocName)
Alexander Hansen97a1c932025-08-20 16:28:52 +0200140{
Alexander Hansen5036d942025-08-20 16:38:59 +0200141 if (boardTypes[upstream] != "Chassis" && boardTypes[upstream] != "Board")
Alexander Hansen97a1c932025-08-20 16:28:52 +0200142 {
Alexander Hansen5036d942025-08-20 16:38:59 +0200143 return;
144 }
145 // The downstream path must be one we care about.
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200146 if (!std::ranges::contains(boardPaths, upstream))
Alexander Hansen5036d942025-08-20 16:38:59 +0200147 {
148 return;
149 }
150
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200151 // quirk: legacy code did not associate from both sides
152 // TODO(alexander): revisit this
Alexander Hansen7b2a77f2025-08-27 12:27:57 +0200153 if (assocName == assocContaining || assocName == assocPoweredBy)
Alexander Hansen5036d942025-08-20 16:38:59 +0200154 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200155 return;
Benjamin Fairf2f5b7a2022-09-09 19:45:02 +0000156 }
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200157
Alexander Hansen7b2a77f2025-08-27 12:27:57 +0200158 result[upstream].insert({assocName.name, assocName.reverse, downstream});
Alexander Hansenca121f52025-08-21 18:25:19 +0200159}
160
Matt Spinler6eb60972023-08-14 16:36:20 -0500161void Topology::remove(const std::string& boardName)
162{
163 // Remove the board from boardNames, and then using the path
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200164 // found in boardNames remove it from ports
Matt Spinler6eb60972023-08-14 16:36:20 -0500165 auto boardFind = boardNames.find(boardName);
166 if (boardFind == boardNames.end())
167 {
168 return;
169 }
170
171 std::string boardPath = boardFind->second;
172
173 boardNames.erase(boardFind);
174
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200175 for (auto& port : ports)
Matt Spinler6eb60972023-08-14 16:36:20 -0500176 {
Alexander Hansen68e2b0f2025-08-21 17:44:18 +0200177 port.second.erase(boardPath);
Matt Spinler6eb60972023-08-14 16:36:20 -0500178 }
179}