Additions to network manager

This patch adds a common method to make changes to the network manager
configuration. Pushed the basic configuration up to a single method,
which checks for minimum required fields, at which point, each
individual configuration change can be applied on top of that.

Pushed network restart functions to a central point.
Set & get hostname
Set & get gateway outside of existing static IP set & get
Set & get gratuitous ARP.

Change-Id: I74e4a9ce22a350fa05d6ce4ac7b09ef96a789b9f
Signed-off-by: David Cobbley <david.j.cobbley@linux.intel.com>
diff --git a/netman.py b/netman.py
index a99e5cc..e419ab6 100755
--- a/netman.py
+++ b/netman.py
@@ -244,9 +244,41 @@
             mac = ipout.split ()[1]
             return mac
 
-    @dbus.service.method(DBUS_NAME, "", "")
-    def test(self):
-        print("TEST")
+    def _checkNetworkForRequiredFields(self, device):
+        parser = SafeConfigParser()
+        parser.optionxform = str
+        parser.read("/etc/systemd/network/00-bmc-" + device + ".network")
+        sections = parser.sections()
+        if "Match" not in sections:
+            parser.add_section("Match")
+            parser.set("Match", "Name", device)
+
+        if "Network" not in sections:
+            parser.add_section("Network")
+
+        if "Link" not in sections:
+            parser.add_section("Link")
+            mac = self._getAddr("mac", device)
+            try:
+                self.validatemac(mac)
+                parser.set("Link", MAC_PROPERTY, mac)
+            except ValueError as e:
+                print("System MAC Address invalid:" + e)
+
+        return parser
+
+    def _upDownNetworkService(self, device, mac=None):
+        print "Restarting networkd service..."
+        subprocess.check_call(["ip", "link", "set", "dev", device, "down"])
+        if mac is not None:
+            subprocess.check_call(["fw_setenv", "ethaddr", mac])
+            subprocess.check_call(
+                ["ip", "link", "set", "dev", device, "address", mac])
+
+        subprocess.check_call(
+            ["systemctl", "restart", "systemd-networkd.service"])
+
+        return 0
 
     @dbus.service.method(DBUS_NAME, "sas", "x")
     def SetNtpServer (self, device, ntpservers):
@@ -289,60 +321,45 @@
         return 0
 
     @dbus.service.method(DBUS_NAME, "s", "x")
-    def EnableDHCP (self, device):
-        if not self._isvaliddev (device) : raise ValueError, "Invalid Device"
+    def EnableDHCP(self, device):
+        if not self._isvaliddev(device):
+            raise ValueError("Invalid Device")
+        parser = self._checkNetworkForRequiredFields(device)
+        parser.set("Network", "DHCP", "yes")
 
-        confFile = "/etc/systemd/network/00-bmc-" + device + ".network"
+        if "DHCP" not in parser.sections():
+            parser.add_section("DHCP")
+        parser.set("DHCP", "ClientIdentifier", "mac")
 
-        print("Making .network file...")
-        try:
-            networkconf = open (confFile, "w+") 
-        except IOError:
-            raise IOError, "Failed to open " + confFile
-            
-        networkconf.write ('[Match]'+ '\n')
-        networkconf.write ('Name=' + (device) + '\n')
-        networkconf.write ('[Network]' + '\n')
-        networkconf.write ('DHCP=yes' + '\n')
-        networkconf.write ('[DHCP]' + '\n')
-        networkconf.write ('ClientIdentifier=mac' + '\n')
-        networkconf.close ()
+        # Write to config file
+        with open("/etc/systemd/network/00-bmc-" + device + ".network", 'w') \
+                as configfile:
+            parser.write(configfile)
 
-        print("Restarting networkd service...")
-        rc = call(["ip", "addr", "flush", device])
-        rc = call(["systemctl", "restart", "systemd-networkd.service"])
-        return rc
+        return (subprocess.check_call(
+            ["systemctl", "restart", "systemd-networkd.service"]))
 
     @dbus.service.method(DBUS_NAME, "ssss", "x")
-    def SetAddress4 (self, device, ipaddr, netmask, gateway):
-        if not self._isvaliddev (device) : raise ValueError, "Invalid Device"
-        if not self._isvalidmask (netmask) : raise ValueError, "Invalid Mask"
-        prefixLen = getPrefixLen (netmask)
+    def SetAddress4(self, device, ipaddr, netmask, gateway):
+        if not self._isvaliddev(device): raise ValueError, "Invalid Device"
+        if not self._isvalidmask(netmask): raise ValueError, "Invalid Mask"
+        prefixLen = getPrefixLen(netmask)
         if prefixLen == 0: raise ValueError, "Invalid Mask"
-        valid = self._isvalidip (ipaddr, netmask)
+        valid = self._isvalidip(ipaddr, netmask)
         if valid != "Valid": raise ValueError, valid + " IP Address"
-        valid = self._isvalidip (gateway)
+        valid = self._isvalidip(gateway)
         if valid != "Valid": raise ValueError, valid + " IP Address"
 
-        confFile = "/etc/systemd/network/00-bmc-" + device + ".network"
+        parser = self._checkNetworkForRequiredFields(device)
+        parser.set("Network", "DHCP", "no")
+        parser.set("Network", "Address", '{}/{}'.format(ipaddr, prefixLen))
+        parser.set("Network", "Gateway", gateway)
+        with open("/etc/systemd/network/00-bmc-" + device + ".network", 'w') \
+                as configfile:
+            parser.write(configfile)
 
-        print("Making .network file...")
-        try:
-            networkconf = open (confFile, "w+") 
-        except IOError:
-            raise IOError, "Failed to open " + confFile
-
-        networkconf.write ('[Match]'+ '\n')
-        networkconf.write ('Name=' + (device) + '\n')
-        networkconf.write ('[Network]' + '\n')
-        networkconf.write ('Address=' + ipaddr + '/' + str(prefixLen) +  '\n')
-        networkconf.write ('Gateway=' + gateway + '\n')
-        networkconf.close()
-
-        print("Restarting networkd service...")
-        rc = call(["ip", "addr", "flush", device])
-        rc = call(["systemctl", "restart", "systemd-networkd.service"])
-        return rc
+        return (subprocess.check_call(
+            ["systemctl", "restart", "systemd-networkd.service"]))
 
     @dbus.service.method(DBUS_NAME, "s", "s")
     def GetAddressType (self, device):
@@ -360,15 +377,21 @@
             print "Using default networkd config file (%s)" % f
             confFile = f
 
-        with open(confFile, "r") as f:
-            for line in f:
-                config = line.split ("=")
-                if (len (config) < 2) : continue
-                if config [0].upper() == "DHCP":
-                    v = config[1].strip().upper()
-                    if (v=="YES" or v=="IPV4" or v=="IPV6"):
-                        return "DHCP"
-        return "STATIC"
+        parser = self._checkNetworkForRequiredFields(device)
+
+        if parser.has_option("Network", "DHCP") is True:
+            mode = parser.get("Network", "DHCP")
+        else:
+            return "Unknown"
+
+        setting = {
+            'yes': 'DHCP',
+            'true': 'DHCP',
+            'no': 'STATIC'
+        }
+        setting.get(mode.lower(), "Unknown")
+
+        return setting
 
     #family, prefixlen, ip, defgw
     @dbus.service.method(DBUS_NAME, "s", "iyss")
@@ -388,6 +411,11 @@
 
         self.validatemac(mac)
 
+        parser = self._checkNetworkForRequiredFields(device)
+        parser.set("Link", MAC_PROPERTY, mac)
+        with open("/etc/systemd/network/00-bmc-" + device + ".network", 'w') as configfile:
+            parser.write(configfile)
+
         rc = subprocess.call(["fw_setenv", "ethaddr", mac])
 
         print("Restarting networkd service...")
@@ -435,6 +463,42 @@
         else:
             return "DNS entries updated Successfully"
 
+    @dbus.service.method(DBUS_NAME, "s", "x")
+    def SetHostname(self, hostname):
+        subprocess.check_call(["hostnamectl", "set-hostname", hostname])
+        subprocess.check_call(
+            ["systemctl", "restart", "systemd-networkd.service"])
+        return 0
+
+    @dbus.service.method(DBUS_NAME, "", "s")
+    def GetHostname(self):
+        value = subprocess.check_output("hostname")
+        return value.rstrip()
+
+    @dbus.service.method(DBUS_NAME, "ss", "x")
+    def SetGateway(self, device, gateway):
+        if not self._isvaliddev(device):
+            raise ValueError("Invalid Device")
+        valid = self._isvalidip(gateway)
+        if valid != "Valid":
+            raise ValueError(valid + " IP Address")
+        parser = self._checkNetworkForRequiredFields(device)
+        if "no" not in parser.get("Network", "DHCP"):
+            raise EnvironmentError("DHCP is on")
+        parser.set("Network", "Gateway", gateway)
+        with open("/etc/systemd/network/00-bmc-" + device + ".network", 'w') \
+                as configfile:
+            parser.write(configfile)
+        return (subprocess.check_call(
+            ["systemctl", "restart", "systemd-networkd.service"]))
+
+    @dbus.service.method(DBUS_NAME, "s", "s")
+    def GetGateway(self, device):
+        if not self._isvaliddev(device):
+            raise ValueError("Invalid Device")
+        return self._getAddr("ip", device)[3]
+
+
 def main():
     dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
     bus = dbus.SystemBus()