topology: generalize association restrictions

Previously association definitions were limited to be created on configs
with `Type` Board and Chassis.

Change the restriction to be defined per association definition and not
hardcoded.

Remove this restriction for `Type` PowerSupply

Tested: next patch

Change-Id: I304a433afc7311ce5b7c350033566969e8d73793
Signed-off-by: Alexander Hansen <alexander.hansen@9elements.com>
diff --git a/src/entity_manager/topology.cpp b/src/entity_manager/topology.cpp
index 52ab478..c86e0e6 100644
--- a/src/entity_manager/topology.cpp
+++ b/src/entity_manager/topology.cpp
@@ -2,9 +2,17 @@
 
 #include "phosphor-logging/lg2.hpp"
 
-const AssocName assocContaining = AssocName("containing", "contained_by");
+const AssocName assocContaining =
+    AssocName("containing", "contained_by", {"Chassis"},
+              {"Board", "Chassis", "PowerSupply"});
 const AssocName assocContainedBy = assocContaining.getReverse();
-const AssocName assocPowering = AssocName("powering", "powered_by");
+
+// Topology tests say that a chassis can be powering another chassis.
+// In case there is any confusion as to why 'Chassis' can have 'powering'
+// association.
+const AssocName assocPowering =
+    AssocName("powering", "powered_by", {"Chassis", "PowerSupply"},
+              {"Board", "Chassis", "PowerSupply"});
 const AssocName assocPoweredBy = assocPowering.getReverse();
 
 const std::vector<AssocName> supportedAssocs = {
@@ -14,13 +22,16 @@
     assocPoweredBy,
 };
 
-AssocName::AssocName(const std::string& name, const std::string& reverse) :
-    name(name), reverse(reverse)
+AssocName::AssocName(const std::string& name, const std::string& reverse,
+                     const std::set<std::string>& allowedOnBoardTypes,
+                     const std::set<std::string>& allowedOnBoardTypesReverse) :
+    name(name), reverse(reverse), allowedOnBoardTypes(allowedOnBoardTypes),
+    allowedOnBoardTypesReverse(allowedOnBoardTypesReverse)
 {}
 
 AssocName AssocName::getReverse() const
 {
-    return {reverse, name};
+    return {reverse, name, allowedOnBoardTypesReverse, allowedOnBoardTypes};
 }
 
 bool AssocName::operator<(const AssocName& other) const
@@ -191,8 +202,12 @@
     BoardPathsView boardPaths, const Path& upstream, const Path& downstream,
     const AssocName& assocName)
 {
-    if (boardTypes[upstream] != "Chassis" && boardTypes[upstream] != "Board")
+    if (!assocName.allowedOnBoardTypes.contains(boardTypes[upstream]))
     {
+        lg2::error(
+            "Cannot create Association Definition {ASSOC} for {PATH} with board type {TYPE}",
+            "ASSOC", assocName.name, "PATH", upstream, "TYPE",
+            boardTypes[upstream]);
         return;
     }
     // The downstream path must be one we care about.
diff --git a/src/entity_manager/topology.hpp b/src/entity_manager/topology.hpp
index a55b995..164b2bf 100644
--- a/src/entity_manager/topology.hpp
+++ b/src/entity_manager/topology.hpp
@@ -16,9 +16,19 @@
     std::string name;
     std::string reverse;
 
-    AssocName(const std::string& name, const std::string& reverse);
+    AssocName(const std::string& name, const std::string& reverse,
+              const std::set<std::string>& allowedOnBoardTypes,
+              const std::set<std::string>& allowedOnBoardTypesReverse);
     AssocName() = delete;
 
+    // The type (e.g. Chassis, Board, Valve, ...) on which the association is
+    // allowed
+    std::set<std::string> allowedOnBoardTypes;
+
+    // The type (e.g. Chassis, Board, Valve, ...) on which the reverse
+    // association is allowed
+    std::set<std::string> allowedOnBoardTypesReverse;
+
     AssocName getReverse() const;
 
     bool operator==(const AssocName& other) const = default;