Add DNS support

This commit adds support for enabling user to specify the
DNS entries and then updating the resolver file with the same

Change-Id: Ib78b822592ea8bdd1f821ccbf1362d96a5021b90
Signed-off-by: Vishwanatha Subbanna <vishwa@linux.vnet.ibm.com>
diff --git a/ethernet_interface.cpp b/ethernet_interface.cpp
index c9bf123..f9cc4e4 100644
--- a/ethernet_interface.cpp
+++ b/ethernet_interface.cpp
@@ -51,6 +51,8 @@
     EthernetInterfaceIntf::dHCPEnabled(dhcpEnabled);
     MacAddressIntf::mACAddress(getMACAddress(intfName));
     EthernetInterfaceIntf::nTPServers(getNTPServersFromConf());
+    EthernetInterfaceIntf::nameservers(getNameServerFromConf());
+
     // Emit deferred signal.
     if (emitSignal)
     {
@@ -362,6 +364,69 @@
     return value;
 }
 
+ServerList EthernetInterface::nameservers(ServerList value)
+{
+    try
+    {
+        EthernetInterfaceIntf::nameservers(value);
+
+        writeConfigurationFile();
+
+        // Currently we don't have systemd-resolved enabled
+        // in the openbmc. Once we update the network conf file,
+        // it should be read by systemd-resolved.service.
+
+        // The other reason to write the resolv conf is,
+        // we don't want to restart the networkd for nameserver change.
+        // as restarting of systemd-networkd takes more then 2 secs
+        writeDNSEntries(value, resolvConfFile);
+    }
+    catch (InternalFailure& e)
+    {
+        log<level::ERR>("Exception processing DNS entries");
+    }
+    return EthernetInterfaceIntf::nameservers();
+}
+
+ServerList EthernetInterface::getNameServerFromConf()
+{
+    fs::path confPath = manager.getConfDir();
+
+    std::string fileName = systemd::config::networkFilePrefix +
+                           interfaceName() +
+                           systemd::config::networkFileSuffix;
+    confPath /= fileName;
+    ServerList servers;
+    try
+    {
+        config::Parser parser(confPath.string());
+        servers = parser.getValues("Network", "DNS");
+    }
+    catch (InternalFailure& e)
+    {
+        log<level::INFO>("Exception getting DNS value from conf file");
+    }
+    return servers;
+}
+
+void EthernetInterface::writeDNSEntries(const ServerList& dnsList,
+                                        const std::string& file)
+{
+    std::fstream outStream(file, std::fstream::out);
+    if (!outStream.is_open())
+    {
+        log<level::ERR>("Unable to open the file",
+                        entry("FILE=%s", file.c_str()));
+        elog<InternalFailure>();
+    }
+
+    outStream << "### Generated manually via dbus settings ###";
+    for(const auto& server : dnsList)
+    {
+        outStream << "nameserver " << server << "\n";
+    }
+}
+
 void EthernetInterface::loadVLAN(VlanId id)
 {
     std::string vlanInterfaceName = interfaceName() + "." +
@@ -512,6 +577,12 @@
          stream << "NTP=" << ntp << "\n";
     }
 
+    //Add the DNS entry
+    for(const auto& dns: EthernetInterfaceIntf::nameservers())
+    {
+         stream << "DNS=" << dns << "\n";
+    }
+
     // Static
     for (const auto& addr : addrs)
     {
diff --git a/ethernet_interface.hpp b/ethernet_interface.hpp
index 8ef1795..5c088bd 100644
--- a/ethernet_interface.hpp
+++ b/ethernet_interface.hpp
@@ -130,6 +130,11 @@
          */
         ServerList nTPServers(ServerList value) override;
 
+        /** @brief sets the DNS/nameservers.
+         *  @param[in] value - vector of DNS servers.
+         */
+        ServerList nameservers(ServerList value) override;
+
         /** @brief create Vlan interface.
          *  @param[in] id- VLAN identifier.
          */
@@ -153,6 +158,9 @@
         using EthernetInterfaceIntf::interfaceName;
         using MacAddressIntf::mACAddress;
 
+        /** @brief Absolute path of the resolv conf file */
+        static constexpr auto resolvConfFile = "/etc/resolv.conf";
+
     protected:
         /** @brief get the info of the ethernet interface.
          *  @return tuple having the link speed,autonegotiation,duplexmode .
@@ -205,6 +213,18 @@
          */
         ServerList getNTPServersFromConf();
 
+        /** @brief write the DNS entries to resolver file.
+         *  @param[in] dnsList - DNS server list which needs to be written.
+         *  @param[in] file    - File to write the name server entries to.
+         */
+        void writeDNSEntries(const ServerList& dnsList,
+                             const std::string& file);
+
+        /** @brief get the name server details from the network conf
+         *
+         */
+        ServerList getNameServerFromConf();
+
         /** @brief Persistent sdbusplus DBus bus connection. */
         sdbusplus::bus::bus& bus;