mapper: Add GetAssociatedSubTreePathsById and GetAssociatedSubTreeById
dbus-interface change in:
https://gerrit.openbmc.org/c/openbmc/phosphor-dbus-interfaces/+/69999
This commit implements two new methods: GetAssociatedSubTreePathsById
and GetAssociatedSubTreeById. These methods retrieve the paths of
associated endpoints corresponding to the provided identifier, filtering
based on their association with specified endpoint interfaces.
GetAssociatedSubTreePathsById returns the D-Bus paths of associated
endpoints, while GetAssociatedSubTreeById retrieves a dictionary of
D-Bus paths of associated endpoints mapped to corresponding services
associated with the provided identifier.
Tested:
'''
busctl call -j "xyz.openbmc_project.ObjectMapper" "/xyz/openbmc_project/object_mapper" \
"xyz.openbmc_project.ObjectMapper" "GetAssociatedSubTreePathsById" ssassas \
"chassis" \
"/xyz/openbmc_project/inventory" \
1 "xyz.openbmc_project.Inventory.Item.Chassis" \
"powered_by" \
1 "xyz.openbmc_project.Inventory.Item.PowerSupply"
{
"type" : "as",
"data" : [
[
"/xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply0",
"/xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply1",
"/xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply2",
"/xyz/openbmc_project/inventory/system/chassis/motherboard/powersupply3"
]
]
}
'''
Another example.
```
busctl call -j xyz.openbmc_project.ObjectMapper /xyz/openbmc_project/object_mapper \
xyz.openbmc_project.ObjectMapper GetAssociatedSubTreePathsById ssassas \
disk_backplane0 \
/xyz/openbmc_project/inventory \
1 xyz.openbmc_project.Inventory.Item.FabricAdapter \
connecting \
1 xyz.openbmc_project.Inventory.Connector.Port
{
"type" : "as",
"data" : [
[
"/xyz/openbmc_project/inventory/system/chassis/motherboard/disk_backplane0/dp0_connector0",
"/xyz/openbmc_project/inventory/system/chassis/motherboard/disk_backplane0/dp0_connector1",
"/xyz/openbmc_project/inventory/system/chassis/motherboard/disk_backplane0/dp0_connector2",
"/xyz/openbmc_project/inventory/system/chassis/motherboard/disk_backplane0/dp0_connector3",
"/xyz/openbmc_project/inventory/system/chassis/motherboard/disk_backplane0/dp0_connector4",
"/xyz/openbmc_project/inventory/system/chassis/motherboard/disk_backplane0/dp0_connector5"
]
]
}
```
Change-Id: Id55a9b41fe70f7204543d92b5396888f6914a1d4
Signed-off-by: Lakshmi Yadlapati <lakshmiy@us.ibm.com>
Signed-off-by: Myung Bae <myungbae@us.ibm.com>
diff --git a/src/handler.cpp b/src/handler.cpp
index 5983ad9..2c4b5d7 100644
--- a/src/handler.cpp
+++ b/src/handler.cpp
@@ -336,3 +336,118 @@
}
return output;
}
+
+// This function works like getSubTreePaths() but only matching id with
+// the leaf-name instead of full path.
+std::vector<std::string> getSubTreePathsById(
+ const InterfaceMapType& interfaceMap, const std::string& id,
+ const std::string& objectPath, std::vector<std::string>& interfaces)
+{
+ std::sort(interfaces.begin(), interfaces.end());
+
+ std::string localObjectPath = objectPath;
+
+ if (!localObjectPath.ends_with("/"))
+ {
+ localObjectPath += "/";
+ }
+ std::string_view objectPathStripped =
+ std::string_view(localObjectPath).substr(0, localObjectPath.size() - 1);
+
+ if (!objectPathStripped.empty() &&
+ interfaceMap.find(objectPathStripped) == interfaceMap.end())
+ {
+ throw sdbusplus::xyz::openbmc_project::Common::Error::
+ ResourceNotFound();
+ }
+
+ std::vector<std::string> output;
+ for (const auto& path : interfaceMap)
+ {
+ const auto& thisPath = path.first;
+
+ // Skip exact match on stripped search term or
+ // the path does not end with the id
+ if (thisPath == objectPathStripped || !thisPath.ends_with("/" + id))
+ {
+ continue;
+ }
+
+ if (thisPath.starts_with(objectPath))
+ {
+ for (const auto& interfaceMap : path.second)
+ {
+ std::vector<std::string> tempoutput(
+ std::min(interfaces.size(), interfaceMap.second.size()));
+ if (std::set_intersection(
+ interfaces.begin(), interfaces.end(),
+ interfaceMap.second.begin(), interfaceMap.second.end(),
+ tempoutput.begin()) != tempoutput.begin())
+ {
+ output.emplace_back(thisPath);
+ break;
+ }
+ }
+ }
+ }
+ if (output.empty())
+ {
+ throw sdbusplus::xyz::openbmc_project::Common::Error::
+ ResourceNotFound();
+ }
+ return output;
+}
+
+std::vector<InterfaceMapType::value_type> getAssociatedSubTreeById(
+ const InterfaceMapType& interfaceMap,
+ const AssociationMaps& associationMaps, const std::string& id,
+ const std::string& objectPath, std::vector<std::string>& subtreeInterfaces,
+ const std::string& association,
+ std::vector<std::string>& endpointInterfaces)
+{
+ std::vector<std::string> subtreePaths =
+ getSubTreePathsById(interfaceMap, id, objectPath, subtreeInterfaces);
+
+ std::vector<InterfaceMapType::value_type> output;
+ for (const auto& subtreePath : subtreePaths)
+ {
+ // Form the association path
+ std::string associationPathStr = subtreePath + "/" + association;
+ sdbusplus::message::object_path associationPath(associationPathStr);
+
+ auto associatedSubTree =
+ getAssociatedSubTree(interfaceMap, associationMaps, associationPath,
+ objectPath, 0, endpointInterfaces);
+
+ output.insert(output.end(), associatedSubTree.begin(),
+ associatedSubTree.end());
+ }
+ return output;
+}
+
+std::vector<std::string> getAssociatedSubTreePathsById(
+ const InterfaceMapType& interfaceMap,
+ const AssociationMaps& associationMaps, const std::string& id,
+ const std::string& objectPath, std::vector<std::string>& subtreeInterfaces,
+ const std::string& association,
+ std::vector<std::string>& endpointInterfaces)
+{
+ std::vector<std::string> subtreePaths =
+ getSubTreePathsById(interfaceMap, id, objectPath, subtreeInterfaces);
+ std::vector<std::string> output;
+ for (const auto& subtreePath : subtreePaths)
+ {
+ // Form the association path
+ std::string associationPathStr = subtreePath + "/" + association;
+ sdbusplus::message::object_path associationPath(associationPathStr);
+
+ auto associatedSubTree = getAssociatedSubTreePaths(
+ interfaceMap, associationMaps, associationPath, objectPath, 0,
+ endpointInterfaces);
+
+ output.insert(output.end(), associatedSubTree.begin(),
+ associatedSubTree.end());
+ }
+
+ return output;
+}
diff --git a/src/handler.hpp b/src/handler.hpp
index 515c937..f6d4d94 100644
--- a/src/handler.hpp
+++ b/src/handler.hpp
@@ -68,3 +68,51 @@
const sdbusplus::message::object_path& associationPath,
const sdbusplus::message::object_path& reqPath, int32_t depth,
std::vector<std::string>& interfaces);
+
+/**
+ * @brief Get the Associated Sub Tree Paths object by id
+ *
+ * @param interfaceMap Mapper Structure storing all associations
+ * @param associationMaps Map of association between objects
+ * @param id Identifier to search for the subtree
+ * @param objectPath Base path to search for the subtree
+ * @param subtreeInterfaces Interface filter for the subtree
+ * @param association The endpoint association
+ * @param endpointInterfaces Interface filter for the endpoint association
+ *
+ * Use getAssociatedSubTree and return only the dbus objects that
+ * are associated with the provided identifier, filtering based on on their
+ * endpoint association.
+ *
+ * @return std::vector<InterfaceMapType::value_type>
+ */
+std::vector<InterfaceMapType::value_type> getAssociatedSubTreeById(
+ const InterfaceMapType& interfaceMap,
+ const AssociationMaps& associationMaps, const std::string& id,
+ const std::string& objectPath, std::vector<std::string>& subtreeInterfaces,
+ const std::string& association,
+ std::vector<std::string>& endpointInterfaces);
+
+/**
+ * @brief Get the Associated Sub Tree Paths object by id
+ *
+ * @param interfaceMap Mapper Structure storing all associations
+ * @param associationMaps Map of association between objects
+ * @param id Identifier to search for the subtree
+ * @param objectPath Base path to search for the subtree
+ * @param subtreeInterfaces Interface filter for the subtree
+ * @param association The endpoint association
+ * @param endpointInterfaces Interface filter for the endpoint association
+ *
+ * Use getAssociatedSubTreePaths and return only the dbus objects that
+ * are associated with the provided identifier, filtering based on on their
+ * endpoint association.
+ *
+ * @return std::vector<std::string>
+ */
+std::vector<std::string> getAssociatedSubTreePathsById(
+ const InterfaceMapType& interfaceMap,
+ const AssociationMaps& associationMaps, const std::string& id,
+ const std::string& objectPath, std::vector<std::string>& subtreeInterfaces,
+ const std::string& association,
+ std::vector<std::string>& endpointInterfaces);
diff --git a/src/main.cpp b/src/main.cpp
index 694acd1..ebaf1ab 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -616,6 +616,28 @@
interfaces);
});
+ iface->register_method(
+ "GetAssociatedSubTreeById",
+ [&interfaceMap](const std::string& id, const std::string& objectPath,
+ std::vector<std::string>& subtreeInterfaces,
+ const std::string& association,
+ std::vector<std::string>& endpointInterfaces) {
+ return getAssociatedSubTreeById(interfaceMap, associationMaps, id,
+ objectPath, subtreeInterfaces,
+ association, endpointInterfaces);
+ });
+
+ iface->register_method(
+ "GetAssociatedSubTreePathsById",
+ [&interfaceMap](const std::string& id, const std::string& objectPath,
+ std::vector<std::string>& subtreeInterfaces,
+ const std::string& association,
+ std::vector<std::string>& endpointInterfaces) {
+ return getAssociatedSubTreePathsById(
+ interfaceMap, associationMaps, id, objectPath,
+ subtreeInterfaces, association, endpointInterfaces);
+ });
+
iface->initialize();
io.post([&]() {
diff --git a/src/test/handler.cpp b/src/test/handler.cpp
index aac2d0f..7f418e5 100644
--- a/src/test/handler.cpp
+++ b/src/test/handler.cpp
@@ -32,7 +32,16 @@
{
"/test/object_path_0/child/grandchild/dog",
{{"test_object_connection_3", {"test_interface_3"}}},
- }};
+ },
+ {
+ "/test/object_path_0/child1",
+ {{"test_object_connection_4", {"test_interface_4"}}},
+ },
+ {
+ "/test/object_path_0/grandchild/child1",
+ {{"test_object_connection_5", {"test_interface_5"}}},
+ },
+ };
AssociationMaps associationMap = {
.ifaces =
@@ -56,6 +65,25 @@
},
},
},
+ {
+ "/test/object_path_0/grandchild/child1/descendent",
+ {
+ std::shared_ptr<sdbusplus::asio::dbus_interface>(),
+ {
+ "/test/object_path_0/child",
+ },
+ },
+ },
+ {
+ "/test/object_path_0/child1/descendent",
+ {
+ std::shared_ptr<sdbusplus::asio::dbus_interface>(),
+ {
+ "/test/object_path_0/child1",
+ "/test/object_path_0/child1/grandchild",
+ },
+ },
+ },
},
.owners = {},
.pending = {},
@@ -79,7 +107,6 @@
"test_interface_1",
}));
ASSERT_EQ(interfaceMaps.size(), 1);
-
auto entry = std::find_if(
interfaceMaps.begin(), interfaceMaps.end(),
[](const auto& i) { return "test_object_path" == i.first; });
@@ -411,3 +438,151 @@
ASSERT_THAT(subtreePath,
ElementsAre("/test/object_path_0/child/grandchild"));
}
+
+TEST_F(TestHandler, getAssociatedSubTreeByIdBad)
+{
+ sdbusplus::message::object_path path("/test/object_path_0");
+ std::vector<std::string> subtreeInterfaces = {"test_interface_1",
+ "test_interface_3"};
+ std::vector<std::string> badsubtreeInterfaces = {"bad_interface"};
+ std::vector<std::string> endpointinvalidInterfaces = {"test_interface_3"};
+ std::vector<std::string> endpointvalidInterfaces = {"test_interface_1",
+ "test_interface_2"};
+ // invalid id
+ EXPECT_THROW(
+ getAssociatedSubTreeById(interfaceMap, associationMap, "childx", path,
+ subtreeInterfaces, "descendent",
+ endpointvalidInterfaces),
+ sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound);
+
+ // invalid subtreeInterfaces
+ EXPECT_THROW(
+ getAssociatedSubTreeById(interfaceMap, associationMap, "child", path,
+ badsubtreeInterfaces, "descendent",
+ endpointvalidInterfaces),
+ sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound);
+
+ // invalid endpointinterface
+ ASSERT_TRUE(getAssociatedSubTreeById(interfaceMap, associationMap, "child",
+ path, subtreeInterfaces, "descendent",
+ endpointinvalidInterfaces)
+ .empty());
+ // valid id, but doesn't have specified interface
+ EXPECT_THROW(
+ getAssociatedSubTreeById(interfaceMap, associationMap, "grandchild",
+ path, subtreeInterfaces, "descendent",
+ endpointvalidInterfaces),
+ sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound);
+
+ // invalid association
+ ASSERT_TRUE(getAssociatedSubTreeById(interfaceMap, associationMap, "child",
+ path, subtreeInterfaces, "dog",
+ endpointinvalidInterfaces)
+ .empty());
+
+ // Invalid path
+ path = sdbusplus::message::object_path("/invalid_path");
+ EXPECT_THROW(
+ getAssociatedSubTreeById(interfaceMap, associationMap, "child", path,
+ subtreeInterfaces, "descendent",
+ endpointvalidInterfaces),
+ sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound);
+}
+
+TEST_F(TestHandler, getAssociatedSubTreeByIdGood)
+{
+ sdbusplus::message::object_path path0("/test/object_path_0");
+ std::vector<std::string> interfaces = {
+ "test_interface_1", "test_interface_2", "test_interface_3"};
+
+ // Path0
+ std::vector<InterfaceMapType::value_type> subtree =
+ getAssociatedSubTreeById(interfaceMap, associationMap, "child", path0,
+ interfaces, "descendent", interfaces);
+ ASSERT_EQ(subtree.size(), 1);
+ ConnectionNames connection = subtree[0].second;
+ auto object = connection.find("test_object_connection_2");
+ ASSERT_NE(object, connection.end());
+ ASSERT_THAT(object->second, ElementsAre("test_interface_2"));
+
+ std::vector<std::string> interfaces1 = {
+ "test_interface_1", "test_interface_4", "test_interface_5"};
+ // Path0 with Depth path of 0
+ subtree =
+ getAssociatedSubTreeById(interfaceMap, associationMap, "child1", path0,
+ interfaces1, "descendent", interfaces1);
+ ASSERT_EQ(subtree.size(), 2);
+}
+
+TEST_F(TestHandler, getAssociatedSubTreePathsByIdBad)
+{
+ sdbusplus::message::object_path path("/test/object_path_0");
+ std::vector<std::string> subtreeInterfaces = {"test_interface_1",
+ "test_interface_3"};
+ std::vector<std::string> badsubtreeInterfaces = {"bad_interface"};
+ std::vector<std::string> endpointinvalidInterfaces = {"test_interface_3"};
+ std::vector<std::string> endpointvalidInterfaces = {"test_interface_1",
+ "test_interface_2"};
+ // invalid id
+ EXPECT_THROW(
+ getAssociatedSubTreePathsById(interfaceMap, associationMap, "childx",
+ path, subtreeInterfaces, "descendent",
+ endpointvalidInterfaces),
+ sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound);
+
+ // invalid subtreeInterfaces
+ EXPECT_THROW(
+ getAssociatedSubTreePathsById(interfaceMap, associationMap, "child",
+ path, badsubtreeInterfaces, "descendent",
+ endpointvalidInterfaces),
+ sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound);
+
+ // invalid endpointinterface
+ ASSERT_TRUE(getAssociatedSubTreePathsById(
+ interfaceMap, associationMap, "child", path,
+ subtreeInterfaces, "descendent", endpointinvalidInterfaces)
+ .empty());
+ // valid id, but doesn't have specified interface
+ EXPECT_THROW(
+ getAssociatedSubTreePathsById(interfaceMap, associationMap,
+ "grandchild", path, subtreeInterfaces,
+ "descendent", endpointvalidInterfaces),
+ sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound);
+
+ // invalid association
+ ASSERT_TRUE(getAssociatedSubTreePathsById(interfaceMap, associationMap,
+ "child", path, subtreeInterfaces,
+ "dog", endpointinvalidInterfaces)
+ .empty());
+
+ // Invalid path
+ path = sdbusplus::message::object_path("/invalid_path");
+ EXPECT_THROW(
+ getAssociatedSubTreePathsById(interfaceMap, associationMap, "child",
+ path, subtreeInterfaces, "descendent",
+ endpointvalidInterfaces),
+ sdbusplus::xyz::openbmc_project::Common::Error::ResourceNotFound);
+}
+
+TEST_F(TestHandler, getAssociatedSubTreePathsByIdGood)
+{
+ sdbusplus::message::object_path path0("/test/object_path_0");
+ std::vector<std::string> interfaces = {
+ "test_interface_1", "test_interface_2", "test_interface_3"};
+
+ // Path0
+ std::vector<std::string> subtreePath = getAssociatedSubTreePathsById(
+ interfaceMap, associationMap, "child", path0, interfaces, "descendent",
+ interfaces);
+ ASSERT_THAT(subtreePath,
+ ElementsAre("/test/object_path_0/child/grandchild"));
+
+ std::vector<std::string> interfaces1 = {
+ "test_interface_1", "test_interface_4", "test_interface_5"};
+ // Path0 with Depth path of 0
+ subtreePath = getAssociatedSubTreePathsById(
+ interfaceMap, associationMap, "child1", path0, interfaces1,
+ "descendent", interfaces1);
+ ASSERT_THAT(subtreePath, ElementsAre("/test/object_path_0/child1",
+ "/test/object_path_0/child"));
+}