GENICLOUD-31 Remove duplicate entries from the bridged interface.
[pyplnet.git] / plnet.py
index b817463..0f692f4 100755 (executable)
--- a/plnet.py
+++ b/plnet.py
@@ -1,11 +1,11 @@
 #!/usr/bin/python /usr/bin/plcsh
-# $Id$
 
 import os
 import socket
 import time
 import tempfile
 import errno
+import struct
 
 import sioc
 import modprobe
@@ -43,7 +43,7 @@ def InitInterfaces(logger, plc, data, root="", files_only=False, program="NodeMa
     failedToGetSettings = False
 
     # NOTE: GetInterfaces/NodeNetworks does not necessarily order the interfaces
-    # returned.  Because 'interface'is decremented as each interface is processed,
+    # returned.  Because 'interface' is decremented as each interface is processed,
     # by the time is_primary=True (primary) interface is reached, the device
     # "eth%s" % interface, is not eth0.  But, something like eth-4, or eth-12.
     # This code sorts the interfaces, placing is_primary=True interfaces first.  
@@ -60,8 +60,8 @@ def InitInterfaces(logger, plc, data, root="", files_only=False, program="NodeMa
     interfaces.reverse()
 
     for interface in interfaces:
-       logger.verbose('net:InitInterfaces interface %d: %r'%(device_id,interface))
-       logger.verbose('net:InitInterfaces macs = %r' % macs)
+        logger.verbose('net:InitInterfaces interface %d: %r'%(device_id,interface))
+        logger.verbose('net:InitInterfaces macs = %r' % macs)
         logger.verbose('net:InitInterfaces ips = %r' % ips)
         # Get interface name preferably from MAC address, falling back
         # on IP address.
@@ -74,50 +74,21 @@ def InitInterfaces(logger, plc, data, root="", files_only=False, program="NodeMa
         else:
             orig_ifname = None
 
-       if orig_ifname:
+        if orig_ifname:
             logger.verbose('net:InitInterfaces orig_ifname = %s' % orig_ifname)
-       
-        details = {}
-        details['ONBOOT']='yes'
-        details['USERCTL']='no'
-        if interface['mac']:
-            details['HWADDR'] = interface['mac']
-        if interface['is_primary']:
-            details['PRIMARY']='yes'
-
-        if interface['method'] == "static":
-            details['BOOTPROTO'] = "static"
-            details['IPADDR'] = interface['ip']
-            details['NETMASK'] = interface['netmask']
-            if interface['is_primary']:
-                gateway = interface['gateway']
-                if interface['dns1']:
-                    details['DNS1'] = interface['dns1']
-                if interface['dns2']:
-                    details['DNS2'] = interface['dns2']
-
-        elif interface['method'] == "dhcp":
-            details['BOOTPROTO'] = "dhcp"
-            details['PERSISTENT_DHCLIENT'] = "yes"
-            if interface['hostname']:
-                details['DHCP_HOSTNAME'] = interface['hostname']
-            else:
-                details['DHCP_HOSTNAME'] = hostname 
-            if not interface['is_primary']:
-                details['DHCLIENTARGS'] = "-R subnet-mask"
 
-        try:
-            plc.GetInterfaceTags()
-            version = 4.3
-        except AttributeError:
-            version = 4.2
+        details = prepDetails(interface, hostname)
 
-        if version == 4.3:
+        if 'interface_tag_ids' in interface:
+            version = 4.3
             interface_tag_ids = "interface_tag_ids"
             interface_tag_id = "interface_tag_id"
+            name_key = "tagname"
         else:
+            version = 4.2
             interface_tag_ids = "nodenetwork_setting_ids"
             interface_tag_id = "nodenetwork_setting_id"
+            name_key = "name"
 
         if len(interface[interface_tag_ids]) > 0:
             try:
@@ -127,16 +98,11 @@ def InitInterfaces(logger, plc, data, root="", files_only=False, program="NodeMa
                     settings = plc.GetNodeNetworkSettings({interface_tag_id:interface[interface_tag_ids]})
             except:
                 logger.log("net:InitInterfaces FATAL: failed call GetInterfaceTags({'interface_tag_id':{%s})"% \
-                           interface['interface_tag_ids'])
+                           interface[interface_tag_ids])
                 failedToGetSettings = True
                 continue # on to the next interface
 
             for setting in settings:
-                # to explicitly set interface name
-                name_key = "name"
-                if version == 4.3:
-                    name_key = "tagname"
-                    
                 settingname = setting[name_key].upper()
                 if settingname in ('IFNAME','ALIAS','CFGOPTIONS','DRIVER'):
                     details[settingname]=setting['value']
@@ -147,6 +113,9 @@ def InitInterfaces(logger, plc, data, root="", files_only=False, program="NodeMa
                            "IWCONFIG", "IWPRIV" ] :
                     details [settingname] = setting['value']
                     details ['TYPE']='Wireless'
+                # Bridge setting
+                elif settingname in [ 'BRIDGE' ]:
+                    details['BRIDGE'] = setting['value']
                 else:
                     logger.log("net:InitInterfaces WARNING: ignored setting named %s"%setting[name_key])
 
@@ -180,20 +149,40 @@ def InitInterfaces(logger, plc, data, root="", files_only=False, program="NodeMa
             else:
                 logger.log("net:InitInterfaces WARNING: interface alias (%s) not matched to an interface"% details['ALIAS'])
             device_id -= 1
+        elif 'BRIDGE' in details:
+            #The bridge inherits the mac of the first attached interface.
+            if 'IFNAME' in details:
+                ifname = details['IFNAME']
+                device_id -= 1
+            elif orig_ifname:
+                ifname = orig_ifname
+                device_id -= 1
+            logger.log('net:InitInterfaces: Bridge detected. Adding %s to devices_map' % ifname)
+            devices_map[ifname] = removeBridgedIfaceDetails(details)
+            bridgeName = details['BRIDGE']
+
+            logger.log('net:InitInterfaces: Adding bridge %s' % bridgeName)
+            bridgeDetails = prepDetails(interface)
+            bridgeDetails['TYPE']   = 'Bridge'
+            devices_map[bridgeName] = bridgeDetails
         else:
-            if ('IFNAME' not in details) and not orig_ifname:
-                ifname="eth%d" % (device_id-1)
-                # should check if $ifname is an eth already defines
+            if 'IFNAME' in details:
+                ifname = details['IFNAME']
+                device_id -= 1
+            elif orig_ifname:
+                ifname = orig_ifname
+                device_id -= 1
+            else:
+                while True:
+                    ifname="eth%d" % (device_id-1)
+                    if ifname not in devices_map:
+                        break
+                    device_id += 1
                 if os.path.exists("%s/ifcfg-%s"%(sysconfig,ifname)):
                     logger.log("net:InitInterfaces WARNING: possibly blowing away %s configuration"%ifname)
-            else:
-               if ('IFNAME' not in details) and orig_ifname:
-                    ifname = orig_ifname
-                else:
-                    ifname = details['IFNAME']
-                device_id -= 1
             devices_map[ifname] = details
-                
+        device_id += 1 
+    logger.log('net:InitInterfaces: Device map: %r' % devices_map)
     m = modprobe.Modprobe()
     try:
         m.input("%s/etc/modprobe.conf" % root)
@@ -227,7 +216,7 @@ def InitInterfaces(logger, plc, data, root="", files_only=False, program="NodeMa
     lo = "ifcfg-lo"
     if lo in ifcfgs: ifcfgs.remove(lo)
 
-    # remove known devices from icfgs list
+    # remove known devices from ifcfgs list
     for (dev, details) in devices_map.iteritems():
         ifcfg = 'ifcfg-'+dev
         if ifcfg in ifcfgs: ifcfgs.remove(ifcfg)
@@ -258,6 +247,7 @@ def InitInterfaces(logger, plc, data, root="", files_only=False, program="NodeMa
 
     # Process ifcfg-$dev changes / additions
     newdevs = []
+    table = 10
     for (dev, details) in devices_map.iteritems():
         (fd, tmpnam) = tempfile.mkstemp(dir=sysconfig)
         f = os.fdopen(fd, "w")
@@ -268,8 +258,8 @@ def InitInterfaces(logger, plc, data, root="", files_only=False, program="NodeMa
         
         # print the configuration values
         for (key, val) in details.iteritems():
-            if key not in ('IFNAME','ALIAS','CFGOPTIONS','DRIVER'):
-                f.write('%s=%s\n' % (key, val))
+            if key not in ('IFNAME','ALIAS','CFGOPTIONS','DRIVER','GATEWAY'):
+                f.write('%s="%s"\n' % (key, val))
 
         # print the configuration specific option values (if any)
         if 'CFGOPTIONS' in details:
@@ -286,7 +276,7 @@ def InitInterfaces(logger, plc, data, root="", files_only=False, program="NodeMa
         # compare whether two files are the same
         def comparefiles(a,b):
             try:
-               logger.verbose("net:InitInterfaces comparing %s with %s" % (a,b))
+                logger.verbose("net:InitInterfaces comparing %s with %s" % (a,b))
                 if not os.path.exists(a): return False
                 fb = open(a)
                 buf_a = fb.read()
@@ -301,6 +291,40 @@ def InitInterfaces(logger, plc, data, root="", files_only=False, program="NodeMa
             except IOError, e:
                 return False
 
+        src_route_changed = False
+        if ('PRIMARY' not in details and 'GATEWAY' in details and
+            details['GATEWAY'] != ''):
+            table += 1
+            (fd, rule_tmpnam) = tempfile.mkstemp(dir=sysconfig)
+            os.write(fd, "from %s lookup %d\n" % (details['IPADDR'], table))
+            os.close(fd)
+            rule_dest = "%s/rule-%s" % (sysconfig, dev)
+            if not comparefiles(rule_tmpnam, rule_dest):
+                os.rename(rule_tmpnam, rule_dest)
+                os.chmod(rule_dest, 0644)
+                src_route_changed = True
+            else:
+                os.unlink(rule_tmpnam)
+            (fd, route_tmpnam) = tempfile.mkstemp(dir=sysconfig)
+            netmask = struct.unpack("I", socket.inet_aton(details['NETMASK']))[0]
+            ip = struct.unpack("I", socket.inet_aton(details['IPADDR']))[0]
+            network = socket.inet_ntoa(struct.pack("I", (ip & netmask)))
+            netmask = socket.ntohl(netmask)
+            i = 0
+            while (netmask & (1 << i)) == 0:
+                i += 1
+            prefix = 32 - i
+            os.write(fd, "%s/%d dev %s table %d\n" % (network, prefix, dev, table))
+            os.write(fd, "default via %s dev %s table %d\n" % (details['GATEWAY'], dev, table))
+            os.close(fd)
+            route_dest = "%s/route-%s" % (sysconfig, dev)
+            if not comparefiles(route_tmpnam, route_dest):
+                os.rename(route_tmpnam, route_dest)
+                os.chmod(route_dest, 0644)
+                src_route_changed = True
+            else:
+                os.unlink(route_tmpnam)
+
         path = "%s/ifcfg-%s" % (sysconfig,dev)
         if not os.path.exists(path):
             logger.verbose('net:InitInterfaces adding configuration for %s' % dev)
@@ -309,7 +333,7 @@ def InitInterfaces(logger, plc, data, root="", files_only=False, program="NodeMa
             os.chmod(path,0644)
             newdevs.append(dev)
             
-        elif not comparefiles(tmpnam,path):
+        elif not comparefiles(tmpnam,path) or src_route_changed:
             logger.verbose('net:InitInterfaces Configuration change for %s' % dev)
             if not files_only:
                 logger.verbose('net:InitInterfaces ifdown %s' % dev)
@@ -361,6 +385,52 @@ def InitInterfaces(logger, plc, data, root="", files_only=False, program="NodeMa
             logger.verbose('net:InitInterfaces bringing up %s' % dev)
             os.system("/sbin/ifup %s" % dev)
 
+##
+# Prepare the interface details.
+#
+def prepDetails(interface, hostname=''):
+    details = {}
+    details['ONBOOT']  = 'yes'
+    details['USERCTL'] = 'no'
+    if interface['mac']:
+        details['HWADDR'] = interface['mac']
+    if interface['is_primary']:
+        details['PRIMARY'] = 'yes'
+
+    if interface['method'] == "static":
+        details['BOOTPROTO'] = "static"
+        details['IPADDR']    = interface['ip']
+        details['NETMASK']   = interface['netmask']
+        details['GATEWAY']   = interface['gateway']
+        if interface['is_primary']:
+            gateway = interface['gateway']
+            if interface['dns1']:
+                details['DNS1'] = interface['dns1']
+            if interface['dns2']:
+                details['DNS2'] = interface['dns2']
+
+    elif interface['method'] == "dhcp":
+        details['BOOTPROTO'] = "dhcp"
+        details['PERSISTENT_DHCLIENT'] = "yes"
+        if interface['hostname']:
+            details['DHCP_HOSTNAME'] = interface['hostname']
+        else:
+            details['DHCP_HOSTNAME'] = hostname
+        if not interface['is_primary']:
+            details['DHCLIENTARGS'] = "-R subnet-mask"
+
+    return details
+
+##
+# Remove duplicate entry from the bridged interface's configuration file.
+#
+def removeBridgedIfaceDetails(details):
+    for key in [ 'PRIMARY', 'PERSISTENT_DHCLIENT', 'DHCLIENTARGS', 'DHCP_HOSTNAME',
+                 'BOOTPROTO', 'IPADDR', 'NETMASK', 'GATEWAY', 'DNS1', 'DNS2' ]:
+        if key in details:
+            del details[key]
+    return details
+
 if __name__ == "__main__":
     import optparse
     import sys
@@ -374,7 +444,10 @@ if __name__ == "__main__":
     parser.add_option("-p", "--program", action="store", type="string",
                       dest="program", default="plnet")
     (options, args) = parser.parse_args()
+    options.root = ''
+    options.verbose = True
     if len(args) != 1 or options.root is None:
+        print sys.argv
         print >>sys.stderr, "Missing root or node_id"
         parser.print_help()
         sys.exit(1)