topology: generalized implementation

Topology code did not support adding arbitrary associations since the
original implementation was mostly about "contained_by" and "powering"
association was bolted on top using a special "powerPaths" collection.

This patch has 2 goals:

- Separate "contained_by" from "powering" association as actual systems
  do not always follow this layout. There can be a PSU powering multiple
  chassis, but not be contained by any of them.

- Enable arbitrary associations to be added to inventory items in the
  future. None are added here, but the foundation is there and they do
  not need a special treatment.

Main change:
```
+    std::unordered_map<PortType, std::map<Path, std::set<AssocName>>> ports;

-    std::unordered_map<PortType, std::set<Path>> upstreamPorts;
-    std::unordered_map<PortType, std::set<Path>> downstreamPorts;
-    std::set<Path> powerPaths;
```

The main difference is that 'powerPaths' is no longer needed as the
underlying meaning (`powered_by` association) is stored in the
set<AssocName>.

The quirk of powerering => contained_by is implemented.

Tested: Topology Unit Test Pass.

Some quirks were added to make the tests pass since they correctly!
assert the limitations from the legacy code.

Reworking the tests to represent the capabilities of the new
implementation can be done in future patches, but for this one, the code
should work the same for the existing callers.

Change-Id: I7cfc752241403f0b661448299f1666f882fa8085
Signed-off-by: Alexander Hansen <alexander.hansen@9elements.com>
diff --git a/src/entity_manager/topology.hpp b/src/entity_manager/topology.hpp
index 39dcae7..9fc3536 100644
--- a/src/entity_manager/topology.hpp
+++ b/src/entity_manager/topology.hpp
@@ -37,20 +37,26 @@
     // such as 'MB Upstream Port'
     void fillAssocsForPortId(
         std::unordered_map<std::string, std::set<Association>>& result,
-        BoardPathsView boardPaths, const std::set<Path>& upstreamPaths,
-        const std::set<Path>& downstreamPaths);
+        BoardPathsView boardPaths,
+        const std::map<Path, std::set<AssocName>>& pathAssocs);
 
     void fillAssocForPortId(
         std::unordered_map<std::string, std::set<Association>>& result,
-        BoardPathsView boardPaths, const Path& upstream,
-        const Path& downstream);
+        BoardPathsView boardPaths, const Path& upstream, const Path& downstream,
+        const AssocName& assocName);
+
+    void addPort(const PortType& port, const Path& path,
+                 const AssocName& assocName);
 
     static std::optional<std::string> getOppositeAssoc(
         const AssocName& assocName);
 
-    std::unordered_map<PortType, std::set<Path>> upstreamPorts;
-    std::unordered_map<PortType, std::set<Path>> downstreamPorts;
-    std::set<Path> powerPaths;
+    // Maps the port name to the participating paths.
+    // each path also has their role(s) in the association.
+    // For example a PSU path which is part of "MB Upstream Port"
+    // will have "powering" role.
+    std::unordered_map<PortType, std::map<Path, std::set<AssocName>>> ports;
+
     std::unordered_map<Path, BoardType> boardTypes;
     std::unordered_map<BoardName, Path> boardNames;
 };