An application for hypervisor network configuration

This application takes care of hypervisor network configuration
by communicating with the bios-settings-mgr in the backend.

It hosts xyz.openbmc_project.Network.Hypervisor.service similar
to that of network service and implements system config, ethernet
and ip interfaces whose properties are mapped to the appropriate
properties in the base bios table.

The flow will be like:
- User fires the redfish command to update hypervisor
network configuration properties
- BMCWeb updates the properties in the dbus object
hosted by this application
- This application would update these new values in
the base bios table

This is divided into multiple commits and this commit
hosts xyz.openbmc_project.Network.Hypervisor.service and
creates the /xyz/openbmc_project/network/hypervisor object.

Tested By:

busctl tree xyz.openbmc_project.Network.Hypervisor
└─/xyz
  └─/xyz/openbmc_project
    └─/xyz/openbmc_project/network
      └─/xyz/openbmc_project/network/hypervisor

Signed-off-by: Asmitha Karunanithi <asmitk01@in.ibm.com>
Change-Id: I2d4582bc9c1dbf72c9963f3e43169b695b34b1ed
diff --git a/src/ibm/hypervisor-network-mgr-src/hyp_network_manager.cpp b/src/ibm/hypervisor-network-mgr-src/hyp_network_manager.cpp
new file mode 100644
index 0000000..ad0b8b4
--- /dev/null
+++ b/src/ibm/hypervisor-network-mgr-src/hyp_network_manager.cpp
@@ -0,0 +1,245 @@
+#include "hyp_network_manager.hpp"
+
+#include "types.hpp"
+#include "util.hpp"
+
+#include <phosphor-logging/elog-errors.hpp>
+#include <phosphor-logging/elog.hpp>
+#include <phosphor-logging/log.hpp>
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server/object.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
+
+using sdbusplus::exception::SdBusError;
+
+class HypNetworkMgr;
+
+namespace phosphor
+{
+namespace network
+{
+using namespace phosphor::logging;
+using InternalFailure =
+    sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+
+const std::string intType = "Integer";
+const std::string strType = "String";
+const std::string enumType = "Enumeration";
+
+using ObjectTree =
+    std::map<std::string, std::map<std::string, std::vector<std::string>>>;
+
+auto HypNetworkMgr::getDBusProp(const std::string& objectName,
+                                const std::string& interface,
+                                const std::string& kw)
+{
+    auto bus = sdbusplus::bus::new_default();
+    auto properties = bus.new_method_call(
+        "xyz.openbmc_project.BIOSConfigManager", objectName.c_str(),
+        "org.freedesktop.DBus.Properties", "Get");
+    properties.append(interface);
+    properties.append(kw);
+    auto result = bus.call(properties);
+
+    if (result.is_method_error())
+    {
+        throw std::runtime_error("Get api failed");
+    }
+    return result;
+}
+
+void HypNetworkMgr::setBIOSTableAttr(
+    std::string attrName, std::variant<std::string, int64_t> attrValue,
+    std::string attrType)
+{
+    auto findAttr = biosTableAttrs.find(attrName);
+    if (findAttr != biosTableAttrs.end())
+    {
+        if (attrType == intType)
+        {
+            int64_t value = std::get<int64_t>(attrValue);
+            if (value != std::get<int64_t>(findAttr->second))
+            {
+                biosTableAttrs.erase(findAttr);
+                biosTableAttrs.emplace(attrName, value);
+            }
+        }
+        else if (attrType == strType)
+        {
+            std::string value = std::get<std::string>(attrValue);
+            if (value != std::get<std::string>(findAttr->second))
+            {
+                biosTableAttrs.erase(findAttr);
+                biosTableAttrs.emplace(attrName, value);
+            }
+        }
+    }
+    else
+    {
+        log<level::INFO>(
+            "setBIOSTableAttr: Attribute is not found in biosTableAttrs"),
+            entry("attrName : ", attrName.c_str());
+    }
+}
+
+void HypNetworkMgr::setBIOSTableAttrs()
+{
+    try
+    {
+        constexpr auto biosMgrIntf = "xyz.openbmc_project.BIOSConfig.Manager";
+        constexpr auto biosMgrObj = "/xyz/openbmc_project/bios_config";
+
+        constexpr auto mapperBus = "xyz.openbmc_project.ObjectMapper";
+        constexpr auto mapperObj = "/xyz/openbmc_project/object_mapper";
+        constexpr auto mapperIntf = "xyz.openbmc_project.ObjectMapper";
+
+        std::vector<std::string> interfaces;
+        interfaces.emplace_back(biosMgrIntf);
+        auto depth = 0;
+
+        auto mapperCall =
+            bus.new_method_call(mapperBus, mapperObj, mapperIntf, "GetSubTree");
+
+        mapperCall.append(biosMgrObj, depth, interfaces);
+
+        auto mapperReply = bus.call(mapperCall);
+        if (mapperReply.is_method_error())
+        {
+            log<level::ERR>("Error in mapper call");
+            elog<InternalFailure>();
+        }
+
+        ObjectTree objectTree;
+        mapperReply.read(objectTree);
+
+        if (objectTree.empty())
+        {
+            log<level::ERR>("No Object has implemented the interface",
+                            entry("INTERFACE=%s", biosMgrIntf));
+            elog<InternalFailure>();
+        }
+
+        std::string objPath;
+
+        if (1 == objectTree.size())
+        {
+            objPath = objectTree.begin()->first;
+        }
+        else
+        {
+            // If there are more than 2 objects, object path must contain the
+            // interface name
+            for (auto const& object : objectTree)
+            {
+                log<level::INFO>("interface", entry("INT=%s", biosMgrIntf));
+                log<level::INFO>("object",
+                                 entry("OBJ=%s", object.first.c_str()));
+
+                if (std::string::npos != object.first.find(biosMgrIntf))
+                {
+                    objPath = object.first;
+                    break;
+                }
+            }
+
+            if (objPath.empty())
+            {
+                log<level::ERR>("Can't find the object for the interface",
+                                entry("intfName=%s", biosMgrIntf));
+                elog<InternalFailure>();
+            }
+        }
+
+        std::variant<BiosBaseTableType> response;
+        getDBusProp(objPath, biosMgrIntf, "BaseBIOSTable").read(response);
+
+        const BiosBaseTableType* baseBiosTable =
+            std::get_if<BiosBaseTableType>(&response);
+
+        if (baseBiosTable == nullptr)
+        {
+            log<level::ERR>("BaseBiosTable is empty. No attributes found!");
+            return;
+        }
+
+        for (const BiosBaseTableItemType& item : *baseBiosTable)
+        {
+            if (item.first.rfind("vmi", 0) == 0) // starts with the prefix
+            {
+                const std::string& itemType =
+                    std::get<biosBaseAttrType>(item.second);
+
+                if (itemType.compare(itemType.size() - intType.size(),
+                                     intType.size(), intType) == 0)
+                {
+                    const int64_t* currValue = std::get_if<int64_t>(
+                        &std::get<biosBaseCurrValue>(item.second));
+                    if (currValue != nullptr)
+                    {
+                        if (item.first == "vmi_if_count")
+                        {
+                            intfCount = *currValue;
+                        }
+                        biosTableAttrs.emplace(item.first, *currValue);
+                    }
+                }
+                else if ((itemType.compare(itemType.size() - strType.size(),
+                                           strType.size(), strType) == 0) ||
+                         (itemType.compare(itemType.size() - enumType.size(),
+                                           enumType.size(), enumType) == 0))
+                {
+                    const std::string* currValue = std::get_if<std::string>(
+                        &std::get<biosBaseCurrValue>(item.second));
+                    if (currValue != nullptr)
+                    {
+                        biosTableAttrs.emplace(item.first, *currValue);
+                    }
+                }
+                else
+                {
+                    log<level::ERR>("Unsupported datatype: The attribute is of "
+                                    "unknown type");
+                }
+            }
+        }
+    }
+    catch (const SdBusError& e)
+    {
+        log<level::ERR>("Error in making dbus call");
+        throw std::runtime_error("DBus call failed");
+    }
+}
+
+uint16_t HypNetworkMgr::getIntfCount()
+{
+    return intfCount;
+}
+
+biosTableType HypNetworkMgr::getBIOSTableAttrs()
+{
+    return biosTableAttrs;
+}
+
+void HypNetworkMgr::createIfObjects()
+{
+    setBIOSTableAttrs();
+
+    if (intfCount == 1)
+    {
+        // TODO: create eth0 object
+        log<level::INFO>("Create eth0 object");
+    }
+    else if (intfCount == 2)
+    {
+        // TODO: create eth0 and eth1 objects
+        log<level::INFO>("Create eth0 and eth1 objects");
+    }
+    else
+    {
+        log<level::ERR>("More than 2 Interfaces");
+        return;
+    }
+}
+
+} // namespace network
+} // namespace phosphor
diff --git a/src/ibm/hypervisor-network-mgr-src/hyp_network_manager.hpp b/src/ibm/hypervisor-network-mgr-src/hyp_network_manager.hpp
new file mode 100644
index 0000000..fa56d96
--- /dev/null
+++ b/src/ibm/hypervisor-network-mgr-src/hyp_network_manager.hpp
@@ -0,0 +1,148 @@
+#pragma once
+
+#include "types.hpp"
+#include "util.hpp"
+
+#include <sdbusplus/bus.hpp>
+#include <sdbusplus/server/object.hpp>
+#include <sdeventplus/source/event.hpp>
+
+namespace phosphor
+{
+namespace network
+{
+
+class HypEthInterface;
+
+using biosAttrName = std::string;
+using biosAttrType = std::string;
+using biosAttrIsReadOnly = bool;
+using biosAttrDispName = std::string;
+using biosAttrHelpText = std::string;
+using biosAttrMenuPath = std::string;
+using biosAttrCurrValue = std::variant<int64_t, std::string>;
+using biosAttrDefaultValue = std::variant<int64_t, std::string>;
+using biosAttrOptions =
+    std::tuple<std::string, std::variant<int64_t, std::string>>;
+
+using biosTableType = std::map<biosAttrName, biosAttrCurrValue>;
+using BiosBaseTableItemType =
+    std::pair<biosAttrName,
+              std::tuple<biosAttrType, biosAttrIsReadOnly, biosAttrDispName,
+                         biosAttrHelpText, biosAttrMenuPath, biosAttrCurrValue,
+                         biosAttrDefaultValue, std::vector<biosAttrOptions>>>;
+using BiosBaseTableType = std::vector<BiosBaseTableItemType>;
+
+enum BiosBaseTableIndex
+{
+    biosBaseAttrType = 0,
+    biosBaseReadonlyStatus,
+    biosBaseDisplayName,
+    biosBaseDescription,
+    biosBaseMenuPath,
+    biosBaseCurrValue,
+    biosBaseDefaultValue,
+    biosBaseOptions
+};
+
+/** @class Manager
+ *  @brief Implementation for the
+ *         xyz.openbmc_project.Network.Hypervisor DBus API.
+ */
+class HypNetworkMgr
+{
+  public:
+    HypNetworkMgr() = delete;
+    HypNetworkMgr(const HypNetworkMgr&) = delete;
+    HypNetworkMgr& operator=(const HypNetworkMgr&) = delete;
+    HypNetworkMgr(HypNetworkMgr&&) = delete;
+    HypNetworkMgr& operator=(HypNetworkMgr&&) = delete;
+    virtual ~HypNetworkMgr() = default;
+
+    /** @brief Constructor to put object onto bus at a dbus path.
+     *  @param[in] bus - Bus to attach to.
+     *  @param[in] event - event.
+     *  @param[in] path - Path to attach at.
+     */
+    HypNetworkMgr(sdbusplus::bus::bus& bus, sdeventplus::Event& event,
+                  const char* path) :
+        bus(bus),
+        event(event), objectPath(path)
+    {
+        // Create the hypervisor eth interface objects
+        createIfObjects();
+    };
+
+    /** @brief Get the BaseBiosTable attributes
+     *
+     * @return attributes list
+     */
+    biosTableType getBIOSTableAttrs();
+
+    /** @brief Set specific attribute and its value to
+     *         the biosTableAttrs data member
+     *
+     * @param[in] attrName  - attribute name in biosTableAttrs
+     * @param[in] attrValue - attribute value
+     * @param[in] attrType  - attribute type
+     *
+     */
+    void setBIOSTableAttr(std::string attrName,
+                          std::variant<std::string, int64_t> attrValue,
+                          std::string attrType);
+
+  private:
+    /**
+     * @brief get Dbus Prop
+     *
+     * @param[in] objectName - dbus Object
+     * @param[in] interface - dbus Interface
+     * @param[in] kw - keyword under the interface
+     *
+     * @return dbus call response
+     */
+    auto getDBusProp(const std::string& objectName,
+                     const std::string& interface, const std::string& kw);
+
+    /** @brief Fetch the interface and the ipaddress details
+     *         from the Bios table and create the hyp ethernet interfaces
+     *         dbus object.
+     */
+    void createIfObjects();
+
+    /** @brief Get the hypervisor eth interfaces count
+     *
+     *  @return number of interfaces
+     */
+    uint16_t getIntfCount();
+
+    /** @brief Setter method for biosTableAttrs data member
+     *         GET operation on the BIOS table to
+     *         read all the hyp attrbutes (name, value pair)
+     *         and push them to biosTableAttrs data member
+     */
+    void setBIOSTableAttrs();
+
+    /** @brief sdbusplus DBus bus connection. */
+    sdbusplus::bus::bus& bus;
+
+    /**  sdevent Event handle. */
+    sdeventplus::Event& event;
+
+    /** @brief object path */
+    std::string objectPath;
+
+    /** @brief Persistent map of EthernetInterface dbus
+     *         objects and their names
+     */
+    std::map<std::string, std::shared_ptr<HypEthInterface>> interfaces;
+
+    /** @brief interface count */
+    uint16_t intfCount;
+
+    /** @brief map of bios table attrs and values */
+    std::map<biosAttrName, biosAttrCurrValue> biosTableAttrs;
+};
+
+} // namespace network
+} // namespace phosphor
diff --git a/src/ibm/hypervisor-network-mgr-src/hyp_network_manager_main.cpp b/src/ibm/hypervisor-network-mgr-src/hyp_network_manager_main.cpp
new file mode 100644
index 0000000..ddb515b
--- /dev/null
+++ b/src/ibm/hypervisor-network-mgr-src/hyp_network_manager_main.cpp
@@ -0,0 +1,31 @@
+#include "hyp_network_manager.hpp"
+
+#include <sdeventplus/event.hpp>
+
+constexpr char DEFAULT_HYP_NW_OBJPATH[] =
+    "/xyz/openbmc_project/network/hypervisor";
+constexpr char HYP_DEFAULT_NETWORK_BUSNAME[] =
+    "xyz.openbmc_project.Network.Hypervisor";
+
+int main(int /*argc*/, char** /*argv*/)
+{
+    auto bus = sdbusplus::bus::new_default();
+
+    // Add sdbusplus ObjectManager
+    sdbusplus::server::manager::manager objManager(bus, DEFAULT_HYP_NW_OBJPATH);
+
+    // Get default event loop
+    auto event = sdeventplus::Event::get_default();
+
+    // Attach the bus to sd_event to service user requests
+    bus.attach_event(event.get(), SD_EVENT_PRIORITY_NORMAL);
+
+    // Create hypervisor network manager dbus object
+    phosphor::network::HypNetworkMgr manager(bus, event,
+                                             DEFAULT_HYP_NW_OBJPATH);
+
+    bus.request_name(HYP_DEFAULT_NETWORK_BUSNAME);
+
+    event.loop();
+    return 0;
+}
diff --git a/src/ibm/hypervisor-network-mgr-src/meson.build b/src/ibm/hypervisor-network-mgr-src/meson.build
new file mode 100644
index 0000000..672c10d
--- /dev/null
+++ b/src/ibm/hypervisor-network-mgr-src/meson.build
@@ -0,0 +1,22 @@
+hyp_default_busname = 'xyz.openbmc_project.Network.Hypervisor'
+
+configure_file(
+  input: 'xyz.openbmc_project.Network.Hypervisor.service.in',
+  output: 'xyz.openbmc_project.Network.Hypervisor.service',
+  configuration: {
+    'SYSTEMD_TARGET': 'multi-user.target',
+    'HYP_DEFAULT_NETWORK_BUSNAME': hyp_default_busname,
+  },
+  install: true,
+  install_dir: dependency('systemd').get_variable(
+    pkgconfig: 'systemdsystemunitdir'))
+
+executable(
+  'hyp-network-manager',
+  'hyp_network_manager_main.cpp',
+  'hyp_network_manager.cpp',
+  implicit_include_directories: false,
+  dependencies: networkd_dep,
+  install: true,
+  install_dir: get_option('bindir'))
+
diff --git a/src/ibm/hypervisor-network-mgr-src/xyz.openbmc_project.Network.Hypervisor.service.in b/src/ibm/hypervisor-network-mgr-src/xyz.openbmc_project.Network.Hypervisor.service.in
new file mode 100644
index 0000000..7bfa7a5
--- /dev/null
+++ b/src/ibm/hypervisor-network-mgr-src/xyz.openbmc_project.Network.Hypervisor.service.in
@@ -0,0 +1,13 @@
+[Unit]
+Description=Hypervisor Network Manager
+After=xyz.openbmc_project.biosconfig_manager.service
+
+[Service]
+ExecStart=/usr/bin/env hyp-network-manager
+SyslogIdentifier=hyp-network-manager
+Restart=always
+Type=dbus
+BusName=xyz.openbmc_project.Network.Hypervisor
+
+[Install]
+WantedBy=multi-user.target
diff --git a/src/ibm/meson.build b/src/ibm/meson.build
new file mode 100644
index 0000000..cc9ec5c
--- /dev/null
+++ b/src/ibm/meson.build
@@ -0,0 +1 @@
+subdir('hypervisor-network-mgr-src')