NVMeVolume: Instantiate Identify Metric

Add MetricStore interface with Identify Metric to namespace.

Tested: b/348660385#comment14
Google-Bug-Id: b/348660385
Signed-off-by: Hao Jiang <jianghao@google.com>
Change-Id: Ic4e00fc5bf39a1a40ccf4c520d54535738a352cc
diff --git a/src/NVMeSubsys.hpp b/src/NVMeSubsys.hpp
index a102ee1..b00a900 100644
--- a/src/NVMeSubsys.hpp
+++ b/src/NVMeSubsys.hpp
@@ -96,6 +96,10 @@
 
   private:
     friend class NVMePlugin;
+
+    template <nvme_identify_cns CNS, class ClockType>
+    friend class IdentifyMetric;
+
     boost::asio::io_context& io;
     sdbusplus::asio::object_server& objServer;
     std::shared_ptr<sdbusplus::asio::connection> conn;
diff --git a/src/NVMeVolume.cpp b/src/NVMeVolume.cpp
index 80ae54e..bfb6150 100644
--- a/src/NVMeVolume.cpp
+++ b/src/NVMeVolume.cpp
@@ -1,5 +1,9 @@
 #include "NVMeVolume.hpp"
 
+#include "NVMeCacheImpl.hpp"
+
+using SchedulerClockType = std::chrono::steady_clock;
+
 NVMeVolume::NVMeVolume(sdbusplus::asio::object_server& objServer,
                        const std::shared_ptr<sdbusplus::asio::connection>& conn,
                        const std::shared_ptr<NVMeSubsystem>& subsys,
@@ -8,7 +12,7 @@
                subsys->volumePath(ns.namespaceId).c_str()),
     NvmeVolumeBase(dynamic_cast<sdbusplus::bus_t&>(*conn),
                    subsys->volumePath(ns.namespaceId).c_str()),
-    path(subsys->volumePath(ns.namespaceId)), objServer(objServer),
+    path(subsys->volumePath(ns.namespaceId)), objServer(objServer), conn(conn),
     subsys(subsys)
 {
     namespaceId(ns.namespaceId, false);
@@ -42,6 +46,61 @@
 
     VolumeBase::emit_added();
     NvmeVolumeBase::emit_added();
+
+    /* create the MetricStore interface */
+    auto scheduler = Scheduler<SchedulerClockType>::getScheduler(path);
+
+    // NOLINTBEGIN(performance-unnecessary-value-param)
+    auto completeCB =
+        [wealSelf{weak_from_this()},
+         path{path}](std::string_view name,
+                     std::shared_ptr<MetricBase<SchedulerClockType>> metric) {
+        auto self = wealSelf.lock();
+        if (!self || !self->metricStore)
+        {
+            // TODO: log_level = INFO
+            std::cerr << std::format(
+                "[{}]Metrics interface for controller expired, update signal skipped.\n",
+                path);
+        }
+        if (!metric->isCacheValid())
+        {
+            // broadcast invalid metric signal
+            self->metricStore->metricUpdated(std::string{name}, {});
+            return;
+        }
+
+        auto data = metric->getCache();
+        std::vector<uint8_t> headerBuf;
+        headerBuf.resize(sizeof(MetricHeader));
+        MetricHeader& header =
+            // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
+            *reinterpret_cast<MetricHeader*>(headerBuf.data());
+        header.lens = boost::endian::little_uint32_t(sizeof(header));
+        header.version = 0;
+        header.dataFormat = 0;
+        header.startTime = boost::endian::little_uint64_t(
+            std::get<0>(data).time_since_epoch().count());
+        header.finishTime = boost::endian::little_uint64_t(
+            std::get<1>(data).time_since_epoch().count());
+        self->metricStore->metricUpdated(std::string{name}, headerBuf);
+    };
+    // NOLINTEND(performance-unnecessary-value-param)
+
+    using IdentifyMetric =
+        IdentifyMetric<NVME_IDENTIFY_CNS_NS, SchedulerClockType>;
+
+    // NOLINTBEGIN(modernize-make-shared)
+    auto identify = std::shared_ptr<IdentifyMetric>(new IdentifyMetric(
+        this->shared_from_this(), scheduler,
+        // Identify info is persist throughout the namespace lifecycle
+        // i.e. from start() to stop()
+        IdentifyMetric::ClockType::duration::max(),
+        std::bind_front(completeCB, IdentifyMetric::identifyIdentifier())));
+    // NOLINTEND(modernize-make-shared)
+
+    metricStore.emplace(conn->get_io_context(), conn, path,
+                        std::move(identify));
 }
 
 std::shared_ptr<NVMeVolume>
@@ -58,6 +117,7 @@
 
 NVMeVolume::~NVMeVolume()
 {
+    metricStore.reset();
     NvmeVolumeBase::emit_removed();
     VolumeBase::emit_removed();
     objServer.remove_interface(deleteInterface);
diff --git a/src/NVMeVolume.hpp b/src/NVMeVolume.hpp
index 1bd8e19..81f8299 100644
--- a/src/NVMeVolume.hpp
+++ b/src/NVMeVolume.hpp
@@ -38,6 +38,9 @@
     const std::string path;
 
   private:
+    template <nvme_identify_cns CNS, class ClockType>
+    friend class IdentifyMetric;
+
     NVMeVolume(sdbusplus::asio::object_server& objServer,
                const std::shared_ptr<sdbusplus::asio::connection>& conn,
                const std::shared_ptr<NVMeSubsystem>& subsys,
@@ -60,5 +63,9 @@
 
     sdbusplus::asio::object_server& objServer;
 
+    std::shared_ptr<sdbusplus::asio::connection> conn;
+
     std::weak_ptr<NVMeSubsystem> subsys;
+
+    std::optional<MetricStore> metricStore;
 };