xref: /openbmc/entity-manager/src/entity_manager/topology.cpp (revision da89d235c12bfc2c19437fecf2d1ca5251523e1c)
1 #include "topology.hpp"
2 
3 #include "phosphor-logging/lg2.hpp"
4 
5 void Topology::addBoard(const std::string& path, const std::string& boardType,
6                         const std::string& boardName,
7                         const nlohmann::json& exposesItem)
8 {
9     auto findType = exposesItem.find("Type");
10     if (findType == exposesItem.end())
11     {
12         return;
13     }
14 
15     boardNames.try_emplace(boardName, path);
16 
17     PortType exposesType = findType->get<std::string>();
18 
19     if (exposesType == "DownstreamPort")
20     {
21         addDownstreamPort(path, exposesItem);
22     }
23     else if (exposesType.ends_with("Port"))
24     {
25         upstreamPorts[exposesType].insert(path);
26     }
27     else
28     {
29         return;
30     }
31     boardTypes[path] = boardType;
32 }
33 
34 void Topology::addDownstreamPort(const Path& path,
35                                  const nlohmann::json& exposesItem)
36 {
37     auto findConnectsTo = exposesItem.find("ConnectsToType");
38     if (findConnectsTo == exposesItem.end())
39     {
40         lg2::error("Board at path {PATH} is missing ConnectsToType", "PATH",
41                    path);
42         return;
43     }
44     PortType connectsTo = findConnectsTo->get<std::string>();
45 
46     downstreamPorts[connectsTo].insert(path);
47     auto findPoweredBy = exposesItem.find("PowerPort");
48     if (findPoweredBy != exposesItem.end())
49     {
50         powerPaths.insert(path);
51     }
52 }
53 
54 std::unordered_map<std::string, std::set<Association>> Topology::getAssocs(
55     BoardPathsView boardPaths)
56 {
57     std::unordered_map<std::string, std::set<Association>> result;
58 
59     // look at each upstream port type
60     for (const auto& upstreamPortPair : upstreamPorts)
61     {
62         auto downstreamMatch = downstreamPorts.find(upstreamPortPair.first);
63 
64         if (downstreamMatch == downstreamPorts.end())
65         {
66             // no match
67             continue;
68         }
69 
70         fillAssocsForPortId(result, boardPaths, upstreamPortPair.second,
71                             downstreamMatch->second);
72     }
73     return result;
74 }
75 
76 void Topology::fillAssocsForPortId(
77     std::unordered_map<std::string, std::set<Association>>& result,
78     BoardPathsView boardPaths, const std::set<Path>& upstreamPaths,
79     const std::set<Path>& downstreamPaths)
80 {
81     for (const Path& upstream : upstreamPaths)
82     {
83         for (const Path& downstream : downstreamPaths)
84         {
85             fillAssocForPortId(result, boardPaths, upstream, downstream);
86         }
87     }
88 }
89 
90 void Topology::fillAssocForPortId(
91     std::unordered_map<std::string, std::set<Association>>& result,
92     BoardPathsView boardPaths, const Path& upstream, const Path& downstream)
93 {
94     if (boardTypes[upstream] != "Chassis" && boardTypes[upstream] != "Board")
95     {
96         return;
97     }
98     // The downstream path must be one we care about.
99     if (!std::ranges::contains(boardPaths, downstream))
100     {
101         return;
102     }
103 
104     std::string assoc = "contained_by";
105     std::optional<std::string> opposite = getOppositeAssoc(assoc);
106 
107     if (!opposite.has_value())
108     {
109         return;
110     }
111 
112     result[downstream].insert({assoc, opposite.value(), upstream});
113 
114     if (powerPaths.contains(downstream))
115     {
116         assoc = "powering";
117         opposite = getOppositeAssoc(assoc);
118         if (!opposite.has_value())
119         {
120             return;
121         }
122 
123         result[downstream].insert({assoc, opposite.value(), upstream});
124     }
125 }
126 
127 const std::set<std::pair<std::string, std::string>> assocs = {
128     {"powering", "powered_by"}, {"containing", "contained_by"},
129     // ... extend as needed
130 };
131 
132 std::optional<std::string> Topology::getOppositeAssoc(
133     const AssocName& assocName)
134 {
135     for (const auto& entry : assocs)
136     {
137         if (entry.first == assocName)
138         {
139             return entry.second;
140         }
141         if (entry.second == assocName)
142         {
143             return entry.first;
144         }
145     }
146 
147     return std::nullopt;
148 }
149 
150 void Topology::remove(const std::string& boardName)
151 {
152     // Remove the board from boardNames, and then using the path
153     // found in boardNames remove it from upstreamPorts and
154     // downstreamPorts.
155     auto boardFind = boardNames.find(boardName);
156     if (boardFind == boardNames.end())
157     {
158         return;
159     }
160 
161     std::string boardPath = boardFind->second;
162 
163     boardNames.erase(boardFind);
164 
165     for (auto& upstreamPort : upstreamPorts)
166     {
167         upstreamPort.second.erase(boardPath);
168     }
169 
170     for (auto& downstreamPort : downstreamPorts)
171     {
172         downstreamPort.second.erase(boardPath);
173     }
174 }
175