1 #include "topology.hpp" 2 3 #include "phosphor-logging/lg2.hpp" 4 5 const AssocName assocContaining = AssocName("containing", "contained_by"); 6 const AssocName assocContainedBy = assocContaining.getReverse(); 7 const AssocName assocPowering = AssocName("powering", "powered_by"); 8 const AssocName assocPoweredBy = assocPowering.getReverse(); 9 10 AssocName::AssocName(const std::string& name, const std::string& reverse) : 11 name(name), reverse(reverse) 12 {} 13 14 AssocName AssocName::getReverse() const 15 { 16 return {reverse, name}; 17 } 18 19 bool AssocName::operator<(const AssocName& other) const 20 { 21 return name < other.name; 22 } 23 24 void Topology::addBoard(const std::string& path, const std::string& boardType, 25 const std::string& boardName, 26 const nlohmann::json& exposesItem) 27 { 28 auto findType = exposesItem.find("Type"); 29 if (findType == exposesItem.end()) 30 { 31 return; 32 } 33 34 boardNames.try_emplace(boardName, path); 35 36 PortType exposesType = findType->get<std::string>(); 37 38 if (exposesType == "DownstreamPort") 39 { 40 addDownstreamPort(path, exposesItem); 41 } 42 else if (exposesType.ends_with("Port")) 43 { 44 addPort(exposesType, path, assocContaining); 45 } 46 else 47 { 48 return; 49 } 50 boardTypes[path] = boardType; 51 } 52 53 void Topology::addDownstreamPort(const Path& path, 54 const nlohmann::json& exposesItem) 55 { 56 auto findConnectsTo = exposesItem.find("ConnectsToType"); 57 if (findConnectsTo == exposesItem.end()) 58 { 59 lg2::error("Board at path {PATH} is missing ConnectsToType", "PATH", 60 path); 61 return; 62 } 63 PortType connectsTo = findConnectsTo->get<std::string>(); 64 65 addPort(connectsTo, path, assocContainedBy); 66 67 auto findPoweredBy = exposesItem.find("PowerPort"); 68 if (findPoweredBy != exposesItem.end()) 69 { 70 addPort(connectsTo, path, assocPowering); 71 } 72 } 73 74 void 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 88 std::unordered_map<std::string, std::set<Association>> Topology::getAssocs( 89 BoardPathsView boardPaths) 90 { 91 std::unordered_map<std::string, std::set<Association>> result; 92 93 // look at each upstream port type 94 for (const auto& port : ports) 95 { 96 fillAssocsForPortId(result, boardPaths, port.second); 97 } 98 return result; 99 } 100 101 void Topology::fillAssocsForPortId( 102 std::unordered_map<std::string, std::set<Association>>& result, 103 BoardPathsView boardPaths, 104 const std::map<Path, std::set<AssocName>>& pathAssocs) 105 { 106 for (const auto& member : pathAssocs) 107 { 108 for (const auto& other : pathAssocs) 109 { 110 if (other.first == member.first) 111 { 112 continue; 113 } 114 for (const auto& assocName : member.second) 115 { 116 // if the other end of the assocation does not declare 117 // the reverse association, do not associate 118 const bool otherAgrees = 119 other.second.contains(assocName.getReverse()); 120 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. 124 if (!otherAgrees && assocName != assocPowering) 125 { 126 continue; 127 } 128 129 fillAssocForPortId(result, boardPaths, member.first, 130 other.first, assocName); 131 } 132 } 133 } 134 } 135 136 void Topology::fillAssocForPortId( 137 std::unordered_map<std::string, std::set<Association>>& result, 138 BoardPathsView boardPaths, const Path& upstream, const Path& downstream, 139 const AssocName& assocName) 140 { 141 if (boardTypes[upstream] != "Chassis" && boardTypes[upstream] != "Board") 142 { 143 return; 144 } 145 // The downstream path must be one we care about. 146 if (!std::ranges::contains(boardPaths, upstream)) 147 { 148 return; 149 } 150 151 // quirk: legacy code did not associate from both sides 152 // TODO(alexander): revisit this 153 if (assocName == assocContaining || assocName == assocPoweredBy) 154 { 155 return; 156 } 157 158 result[upstream].insert({assocName.name, assocName.reverse, downstream}); 159 } 160 161 void Topology::remove(const std::string& boardName) 162 { 163 // Remove the board from boardNames, and then using the path 164 // found in boardNames remove it from ports 165 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 175 for (auto& port : ports) 176 { 177 port.second.erase(boardPath); 178 } 179 } 180