NVMePlugin: Add VU metric

Add a new function `getMetric` to the plugin to fetch VU NVMe Metric
from a plugin.

The function will be call upon the creation of a metric store with given
the path of the metric store.

The VU NVMe Metric will be inserted into the target metric store
afterwards.

Google-Bug-Id: 348660385#comment14
Tested: b/348660385#comment15
Signed-off-by: Hao Jiang <jianghao@google.com>
Change-Id: I81d1b569cd716c392d9445b08cd3fff64cbf0790
diff --git a/src/NVMeMetricStore.hpp b/src/NVMeMetricStore.hpp
index f88b4d5..4456997 100644
--- a/src/NVMeMetricStore.hpp
+++ b/src/NVMeMetricStore.hpp
@@ -58,6 +58,19 @@
         metricStore{
             {std::string{metrics->getIdentifier()}, std::move(metrics)}...}
     {
+        auto vuMetrics = getVendorMatrics(path);
+        for (auto& metric : vuMetrics)
+        {
+            auto [itr, res] = metricStore.try_emplace(
+                std::string{metric->getIdentifier()}, std::move(metric));
+            if (!res)
+            {
+                std::cerr << std::format(
+                    "[{}, {}] drop a duplicated vendor metic\n", path,
+                    itr->first);
+            }
+        }
+
         std::vector<std::string> idCollection;
         for (auto& [identifier, metric] : metricStore)
         {
@@ -73,6 +86,8 @@
     }
 
   private:
+    static std::vector<std::shared_ptr<MetricBase<>>>
+        getVendorMatrics(const std::string& path);
     /**
      * @brief Writes to file descriptor after getting results from the
      * registered callback function
diff --git a/src/NVMePlugin.cpp b/src/NVMePlugin.cpp
index 7b31e37..51ac4e0 100644
--- a/src/NVMePlugin.cpp
+++ b/src/NVMePlugin.cpp
@@ -1,9 +1,16 @@
 #include "NVMePlugin.hpp"
 
 #include "NVMeIntf.hpp"
+#include "NVMeMetricStore.hpp"
 #include "NVMeSubsys.hpp"
 #include "Utils.hpp"
 
+// map from plugin path (subsystem path) to getMetric function
+std::unordered_map<std::string,
+                   std::function<std::vector<std::shared_ptr<MetricBase<>>>(
+                       const std::string& path)>>
+    pluginMetricFunctions;
+
 std::shared_ptr<NVMeControllerPlugin>
     NVMePlugin::createControllerPlugin(const NVMeController& controller,
                                        const SensorData& config)
@@ -25,6 +32,22 @@
     return res->second.second;
 }
 
+NVMePlugin::NVMePlugin(std::shared_ptr<NVMeSubsystem> subsys,
+                       const SensorData& /*config*/) :
+    subsystem(std::move(subsys))
+{
+    // register the path to metric function table.
+    auto [_, res] = pluginMetricFunctions.try_emplace(
+        getPath(), std::bind_front(&NVMePlugin::getMetric, this));
+    assert(res && "found duplicate plugin path for metric function");
+}
+
+NVMePlugin::~NVMePlugin()
+{
+    // remove the metric function
+    pluginMetricFunctions.erase(getPath());
+}
+
 const std::string& NVMePlugin::getPath() const
 {
     return subsystem->path;
@@ -98,3 +121,45 @@
 {
     return nvmeController->getCntrlId();
 }
+
+std::vector<std::shared_ptr<MetricBase<>>>
+    MetricStore::getVendorMatrics(const std::string& path)
+{
+    auto itr = std::find_if(
+        pluginMetricFunctions.begin(), pluginMetricFunctions.end(),
+        [&path](const std::pair<
+                std::string,
+                std::function<std::vector<std::shared_ptr<MetricBase<>>>(
+                    const std::string& path)>>& pair) {
+        // check if the given path is subdir of the subsystem path
+        const std::string& parent = pair.first;
+        if (parent.empty() || path.empty())
+        {
+            return false;
+        }
+        if (parent.size() > path.size())
+        {
+            return false;
+        }
+        if (!path.starts_with(parent))
+        {
+            return false;
+        }
+        if (parent.back() != '/' && path.size() > parent.size() &&
+            path.at(parent.size()) != '/')
+        {
+            return false;
+        }
+
+        return true;
+    });
+    if (itr == pluginMetricFunctions.end())
+    {
+        return {};
+    }
+    if (!itr->second)
+    {
+        return {};
+    }
+    return itr->second(path);
+}
diff --git a/src/NVMePlugin.hpp b/src/NVMePlugin.hpp
index b45e718..88ed189 100644
--- a/src/NVMePlugin.hpp
+++ b/src/NVMePlugin.hpp
@@ -1,5 +1,6 @@
 #pragma once
 
+#include "NVMeCache.hpp"
 #include "Utils.hpp"
 
 #include <libnvme-mi.h>
@@ -97,10 +98,9 @@
 class NVMePlugin
 {
   public:
-    NVMePlugin(std::shared_ptr<NVMeSubsystem> subsys,
-               const SensorData& /*config*/) : subsystem(std::move(subsys)) {};
+    NVMePlugin(std::shared_ptr<NVMeSubsystem> subsys, const SensorData& config);
 
-    virtual ~NVMePlugin() = default;
+    virtual ~NVMePlugin();
 
     std::shared_ptr<NVMeControllerPlugin>
         createControllerPlugin(const NVMeController& controller,
@@ -114,6 +114,13 @@
     // itself.
     virtual void stop() {}
 
+    // get Vendor Metric for given path
+    virtual std::vector<std::shared_ptr<MetricBase<>>>
+        getMetric(const std::string& /*path*/) noexcept
+    {
+        return {};
+    }
+
     static constexpr const char* libraryPath = "/usr/lib/nvmed/";
 
   protected: