Add Location information for Fabric Port

This commit is to add location information to Port of FabricAdapter.
If Port LocationCode property is not found, it will not be shown.

Tested:
  - Redfish Validator passed
  - Check Location from GET Port output

```
% curl -k -X GET https://${bmc}:18080/redfish/v1/Systems/system/FabricAdapters/disk_backplane0/Ports/dp0_connector4
{
  "@odata.id": "/redfish/v1/Systems/system/FabricAdapters/disk_backplane0/Ports/dp0_connector4",
  "@odata.type": "#Port.v1_7_0.Port",
  "Id": "dp0_connector4",
  "Location": {
    "PartLocation": {
      "ServiceLabel": "U78DA.ND0.WZS003T-P1-T4"
    }
  },
  "Name": "dp0_connector4"
}
```

Change-Id: Ie015b19612c03a9c656ad14a3f607da04cb4f901
Signed-off-by: Myung Bae <myungbae@us.ibm.com>
diff --git a/docs/Redfish.md b/docs/Redfish.md
index 0575962..b73ceb1 100644
--- a/docs/Redfish.md
+++ b/docs/Redfish.md
@@ -865,7 +865,7 @@
 
 #### Port
 
-- no properties
+- Location
 
 ### /redfish/v1/Systems/system/LogServices/
 
diff --git a/redfish-core/lib/fabric_ports.hpp b/redfish-core/lib/fabric_ports.hpp
index 24d5e51..854b398 100644
--- a/redfish-core/lib/fabric_ports.hpp
+++ b/redfish-core/lib/fabric_ports.hpp
@@ -6,6 +6,7 @@
 
 #include "app.hpp"
 #include "async_resp.hpp"
+#include "dbus_singleton.hpp"
 #include "dbus_utility.hpp"
 #include "error_messages.hpp"
 #include "http_request.hpp"
@@ -14,10 +15,13 @@
 #include "query.hpp"
 #include "registries/privilege_registry.hpp"
 
+#include <asm-generic/errno.h>
+
 #include <boost/beast/http/field.hpp>
 #include <boost/beast/http/verb.hpp>
 #include <boost/system/error_code.hpp>
 #include <boost/url/format.hpp>
+#include <sdbusplus/asio/property.hpp>
 
 #include <algorithm>
 #include <array>
@@ -36,10 +40,38 @@
 static constexpr std::array<std::string_view, 1> portInterfaces{
     "xyz.openbmc_project.Inventory.Connector.Port"};
 
+inline void afterGetFabricPortLocation(
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const boost::system::error_code& ec, const std::string& value)
+{
+    if (ec)
+    {
+        if (ec.value() != EBADR)
+        {
+            BMCWEB_LOG_ERROR("DBUS response error {}", ec.value());
+            messages::internalError(asyncResp->res);
+        }
+        return;
+    }
+    asyncResp->res.jsonValue["Location"]["PartLocation"]["ServiceLabel"] =
+        value;
+}
+
+inline void getFabricPortLocation(
+    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+    const std::string& portPath, const std::string& serviceName)
+{
+    dbus::utility::getProperty<std::string>(
+        serviceName, portPath,
+        "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode",
+        std::bind_front(afterGetFabricPortLocation, asyncResp));
+}
+
 inline void getFabricPortProperties(
     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
     const std::string& systemName, const std::string& adapterId,
-    const std::string& portId, const std::string& portPath)
+    const std::string& portId, const std::string& portPath,
+    const std::string& serviceName)
 {
     if (portPath.empty())
     {
@@ -58,12 +90,14 @@
                             systemName, adapterId, portId);
     asyncResp->res.jsonValue["Id"] = portId;
     asyncResp->res.jsonValue["Name"] = "Fabric Port";
+    getFabricPortLocation(asyncResp, portPath, serviceName);
 }
 
 inline void afterGetValidFabricPortPath(
     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
     const std::string& portId,
-    std::function<void(const std::string&)>& callback,
+    std::function<void(const std::string& portPath,
+                       const std::string& portServiceName)>& callback,
     const boost::system::error_code& ec,
     const dbus::utility::MapperGetSubTreePathsResponse& portSubTreePaths)
 {
@@ -76,7 +110,7 @@
             return;
         }
         // Port not found
-        callback(std::string());
+        callback(std::string(), std::string());
         return;
     }
     const auto& it =
@@ -87,7 +121,7 @@
     if (it == portSubTreePaths.end())
     {
         // Port not found
-        callback(std::string());
+        callback(std::string(), std::string());
         return;
     }
 
@@ -104,14 +138,15 @@
                 messages::internalError(asyncResp->res);
                 return;
             }
-            callback(portPath);
+            callback(portPath, object.begin()->first);
         });
 }
 
 inline void getValidFabricPortPath(
     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
     const std::string& adapterId, const std::string& portId,
-    std::function<void(const std::string&)>&& callback)
+    std::function<void(const std::string& portPath,
+                       const std::string& portServiceName)>&& callback)
 {
     dbus::utility::getAssociatedSubTreePathsById(
         adapterId, "/xyz/openbmc_project/inventory", fabricInterfaces,
@@ -146,7 +181,7 @@
 
     getValidFabricPortPath(
         asyncResp, adapterId, portId,
-        [asyncResp, portId](const std::string& portPath) {
+        [asyncResp, portId](const std::string& portPath, const std::string&) {
             if (portPath.empty())
             {
                 BMCWEB_LOG_WARNING("Port not found");