Enabling NVMe sensor support

This commit introduces the support for NVMe drives for sensors.

All the NVMe drives which are detected by the FRU manager and are
present in the inventory are scanned at regular interval for reading
the temperature values of the NVMe devices.

Tested:

NAME                                          TYPE      SIGNATURE RESULT/VALUE
org.freedesktop.DBus.Introspectable           interface -         -
.Introspect                                   method    -         s
org.freedesktop.DBus.Peer                     interface -         -
.GetMachineId                                 method    -         s
.Ping                                         method    -         -
org.freedesktop.DBus.Properties               interface -         -
.Get                                          method    ss        v
.GetAll                                       method    s         a{sv}
.Set                                          method    ssv       -
.PropertiesChanged                            signal    sa{sv}as  -
xyz.openbmc_project.Association.Definitions   interface -         -
.Associations                                 property  a(sss)    1 "chassis" "all_sensors" "/xyz/openb...
xyz.openbmc_project.Sensor.Threshold.Critical interface -         -
.CriticalAlarmHigh                            property  b         false
.CriticalAlarmLow                             property  b         false
.CriticalHigh                                 property  d         115
.CriticalLow                                  property  d         0
xyz.openbmc_project.Sensor.Threshold.Warning  interface -         -
.WarningAlarmHigh                             property  b         false
.WarningAlarmLow                              property  b         false
.WarningHigh                                  property  d         110
.WarningLow                                   property  d         5
xyz.openbmc_project.Sensor.Value              interface -         -
.MaxValue                                     property  d         127
.MinValue                                     property  d         -60
.Value                                        property  d         22

Change-Id: Icb119b424234d548c8ff5cda9c7a9517ec9696bb
Signed-off-by: Nikhil Potade <nikhil.potade@linux.intel.com>
Signed-off-by: James Feist <james.feist@linux.intel.com>
diff --git a/src/NVMeSensorMain.cpp b/src/NVMeSensorMain.cpp
new file mode 100644
index 0000000..1858ea9
--- /dev/null
+++ b/src/NVMeSensorMain.cpp
@@ -0,0 +1,181 @@
+/*
+// Copyright (c) 2019 Intel Corporation
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+*/
+
+#include "NVMeSensor.hpp"
+
+#include <boost/asio/deadline_timer.hpp>
+#include <regex>
+
+static constexpr const char* sensorType =
+    "xyz.openbmc_project.Configuration.NVME1000";
+
+static NVMEMap nvmeDeviceMap;
+
+static constexpr bool DEBUG = false;
+
+NVMEMap& getNVMEMap()
+{
+    return nvmeDeviceMap;
+}
+
+void createSensors(boost::asio::io_service& io,
+                   sdbusplus::asio::object_server& objectServer,
+                   std::shared_ptr<sdbusplus::asio::connection>& dbusConnection)
+{
+
+    auto getter = std::make_shared<GetSensorConfiguration>(
+        dbusConnection,
+        std::move([&io, &objectServer, &dbusConnection](
+                      const ManagedObjectType& sensorConfigurations) {
+            // todo: it'd be better to only update the ones we care about
+            nvmeDeviceMap.clear();
+
+            // iterate through all found configurations
+            for (const std::pair<sdbusplus::message::object_path, SensorData>&
+                     sensor : sensorConfigurations)
+            {
+                const SensorData& sensorData = sensor.second;
+                const std::string& interfacePath = sensor.first.str;
+                const std::pair<
+                    std::string,
+                    boost::container::flat_map<std::string, BasicVariantType>>*
+                    baseConfiguration = nullptr;
+
+                // find base configuration
+                auto sensorBase = sensor.second.find(sensorType);
+                if (sensorBase != sensor.second.end())
+                {
+                    baseConfiguration = &(*sensorBase);
+                }
+
+                if (baseConfiguration == nullptr)
+                {
+                    continue;
+                }
+                auto findBus = baseConfiguration->second.find("Bus");
+                if (findBus == baseConfiguration->second.end())
+                {
+                    continue;
+                }
+
+                unsigned int busNumber =
+                    std::visit(VariantToUnsignedIntVisitor(), findBus->second);
+
+                auto findSensorName = baseConfiguration->second.find("Name");
+                if (findSensorName == baseConfiguration->second.end())
+                {
+                    std::cerr << "could not determine configuration name for "
+                              << interfacePath << "\n";
+                    continue;
+                }
+                std::string sensorName =
+                    std::get<std::string>(findSensorName->second);
+
+                std::vector<thresholds::Threshold> sensorThresholds;
+
+                if (!parseThresholdsFromConfig(sensorData, sensorThresholds))
+                {
+                    std::cerr << "error populating thresholds for "
+                              << sensorName << "\n";
+                }
+
+                int rootBus = busNumber;
+
+                std::string muxPath = "/sys/bus/i2c/devices/i2c-" +
+                                      std::to_string(busNumber) + "/mux_device";
+
+                if (std::filesystem::is_symlink(muxPath))
+                {
+                    std::string rootName =
+                        std::filesystem::read_symlink(muxPath).filename();
+                    size_t dash = rootName.find("-");
+                    if (dash == std::string::npos)
+                    {
+                        std::cerr << "Error finding root bus for " << rootName
+                                  << "\n";
+                        continue;
+                    }
+                    rootBus = std::stoi(rootName.substr(0, dash));
+                }
+
+                std::shared_ptr<NVMeContext> context;
+                auto findRoot = nvmeDeviceMap.find(rootBus);
+                if (findRoot != nvmeDeviceMap.end())
+                {
+                    context = findRoot->second;
+                }
+                else
+                {
+                    context = std::make_shared<NVMeContext>(io, rootBus);
+                    nvmeDeviceMap[rootBus] = context;
+                }
+
+                std::shared_ptr<NVMeSensor> sensorPtr =
+                    std::make_shared<NVMeSensor>(
+                        objectServer, io, dbusConnection, sensorName,
+                        std::move(sensorThresholds), interfacePath, busNumber);
+
+                context->sensors.emplace_back(sensorPtr);
+            }
+            for (const auto& [_, context] : nvmeDeviceMap)
+            {
+                context->pollNVMeDevices();
+            }
+        }));
+    getter->getConfiguration(std::vector<std::string>{sensorType});
+}
+
+int main()
+{
+    boost::asio::io_service io;
+    auto systemBus = std::make_shared<sdbusplus::asio::connection>(io);
+    systemBus->request_name("xyz.openbmc_project.NVMeSensor");
+    sdbusplus::asio::object_server objectServer(systemBus);
+    nvmeMCTP::init();
+
+    io.post([&]() { createSensors(io, objectServer, systemBus); });
+
+    boost::asio::deadline_timer filterTimer(io);
+    std::function<void(sdbusplus::message::message&)> eventHandler =
+        [&filterTimer, &io, &objectServer,
+         &systemBus](sdbusplus::message::message& message) {
+            // this implicitly cancels the timer
+            filterTimer.expires_from_now(boost::posix_time::seconds(1));
+
+            filterTimer.async_wait([&](const boost::system::error_code& ec) {
+                if (ec == boost::asio::error::operation_aborted)
+                {
+                    return; // we're being canceled
+                }
+                else if (ec)
+                {
+                    std::cerr << "Error: " << ec.message() << "\n";
+                    return;
+                }
+
+                createSensors(io, objectServer, systemBus);
+            });
+        };
+
+    sdbusplus::bus::match::match configMatch(
+        static_cast<sdbusplus::bus::bus&>(*systemBus),
+        "type='signal',member='PropertiesChanged',path_namespace='" +
+            std::string(inventoryPath) + "',arg0namespace='" +
+            std::string(sensorType) + "'",
+        eventHandler);
+
+    io.run();
+}