Update for iproute-2.6.28
[nodemanager-topo.git] / topo.py
diff --git a/topo.py b/topo.py
index 88327fd..a9a9853 100755 (executable)
--- a/topo.py
+++ b/topo.py
@@ -10,11 +10,18 @@ import logger
 import subprocess
 import sioc
 import re
 import subprocess
 import sioc
 import re
+import vserver
+import os
+from time import strftime
 
 
-dryrun=0
-setup_link_cmd="/usr/share/vini/setup-egre-link"
-teardown_link_cmd="/usr/share/vini/teardown-egre-link"
+dryrun = 0
+vinidir = "/usr/share/vini/"
+setup_link_cmd = vinidir + "setup-egre-link"
+teardown_link_cmd = vinidir + "teardown-egre-link"
+setup_nat_cmd = vinidir + "setup-nat"
+teardown_nat_cmd = vinidir + "teardown-nat"
 ifaces = {}
 ifaces = {}
+old_ifaces = {}
 
 def run(cmd):
     if dryrun:
 
 def run(cmd):
     if dryrun:
@@ -25,7 +32,14 @@ def run(cmd):
 
 
 """
 
 
 """
-Check for existence of interface a<key>x<nodeid>
+Subnet used for virtual interfaces by setup-egre-link script
+"""
+def iias_network():
+    return "192.168.0.0 255.255.0.0"
+
+
+"""
+Check for existence of interface d<key>x<nodeid>
 """
 def virtual_link(key, nodeid):
     name = "d%sx%s" % (key, nodeid)
 """
 def virtual_link(key, nodeid):
     name = "d%sx%s" % (key, nodeid)
@@ -34,28 +48,80 @@ def virtual_link(key, nodeid):
     else:
         return False
 
     else:
         return False
 
-
 """
 Create a "virtual link" for slice between here and nodeid.
 The key is used to create the EGRE tunnel.
 """
 """
 Create a "virtual link" for slice between here and nodeid.
 The key is used to create the EGRE tunnel.
 """
-def setup_virtual_link(slice, key, rate, myid, nodeid, ipaddr):
-    if myid < nodeid:
-        virtip = "10.%d.%d.2" % (myid, nodeid)
-    else:
-        virtip = "10.%d.%d.3" % (nodeid, myid)
-        
-    run(setup_link_cmd + " %s %s %s %s %s %s" % (slice, nodeid, ipaddr, 
-                                                 key, rate, virtip))
+def setup_virtual_link(slice, key, rate, myid, nodeid, ipaddr, virtip, vnet):
+    logger.log("%s: Set up virtual link to node %d" % (slice, nodeid))
+    run(setup_link_cmd + " %s %s %s %s %s %s %s" % (slice, nodeid, ipaddr, 
+                                                 key, rate, virtip, vnet))
     return
 
 
 """
 Tear down the "virtual link" for slice between here and nodeid.
 """
     return
 
 
 """
 Tear down the "virtual link" for slice between here and nodeid.
 """
-def teardown_virtual_link(slice, key, nodeid):
-    logger.log("Tear down virtual link to node %d" % nodeid)
-    run(teardown_link_cmd + " %s %s %s" % (slice, nodeid, key))
+def teardown_virtual_link(key, nodeid):
+    logger.log("topo: Tear down virtual link %sx%s" % (key, nodeid))
+    run(teardown_link_cmd + " %s %s" % (nodeid, key))
+    return
+
+
+"""
+Called for all active virtual link interfaces, so they won't be cleaned up.
+"""
+def refresh_virtual_link(nodeid, key):
+    name = "d%sx%s" % (key, nodeid)
+    if name in old_ifaces:
+        del old_ifaces[name]
+    return
+
+
+"""
+IP address of the NAT interface created inside the slice by the
+setup-nat script.
+"""
+def nat_inner_ip(key):
+    return "10.0.%s.2" % key
+
+
+"""
+Check for existence of interface natx<key>
+"""
+def nat_exists(key):
+    name = "natx%s" % key
+    if name in ifaces:
+        return True
+    else:
+        return False
+
+
+"""
+Create a NAT interface inside the sliver.  
+"""
+def setup_nat(slice, myid, key):
+    logger.log("%s: Set up NAT" % slice)
+    run(setup_nat_cmd + " %s %s %s" % (slice, myid, key))
+    return
+
+
+"""
+Tear down the NAT interface identified by key
+"""
+def teardown_nat(key):
+    logger.log("topo: Tear down NAT %s" % key)
+    run(teardown_nat_cmd + " %s" % key)
+    return
+
+
+"""
+Called for all active NAT interfaces, so they won't be cleaned up.
+"""
+def refresh_nat(key):
+    name = "natx%s" % (key)
+    if name in old_ifaces:
+        del old_ifaces[name]
     return
 
 
     return
 
 
@@ -63,14 +129,21 @@ def teardown_virtual_link(slice, key, nodeid):
 Clean up old virtual links (e.g., to nodes that have been deleted 
 from the slice).
 """
 Clean up old virtual links (e.g., to nodes that have been deleted 
 from the slice).
 """
-def clean_up_old_virtual_links(slice, key, nodelist):
-    pattern = "d%sx(.*)" % key
-    for iface in ifaces:
-        m = re.match(pattern, iface)
+def clean_up_old_virtual_links():
+    pattern1 = "d(.*)x(.*)"
+    pattern2 = "natx(.*)"
+    for iface in old_ifaces:
+        m = re.match(pattern1, iface)
         if m:
         if m:
-            node = m.group(1)
-            if not node in nodelist:
-                teardown_virtual_link(slice, key, node)
+            key = int(m.group(1))
+            node = int(m.group(2))
+            teardown_virtual_link(key, node)
+
+        m = re.match(pattern2, iface)
+        if m:
+            key = int(m.group(1))
+            teardown_nat(key)
+    return
 
 
 """
 
 
 """
@@ -83,38 +156,229 @@ def convert_topospec_to_list(rspec):
 """
 Update virtual links for the slice
 """
 """
 Update virtual links for the slice
 """
-def update(slice, myid, topospec, key):
+def update_links(slice, myid, topospec, key, netns):
     topolist = convert_topospec_to_list(topospec)
     topolist = convert_topospec_to_list(topospec)
-    nodelist=[]
-    for (nodeid,ipaddr,rate) in topolist:
-        nodelist.append(nodeid)
+    for (nodeid, ipaddr, rate, myvirtip, remvirtip, virtnet) in topolist:
         if not virtual_link(key, nodeid):
         if not virtual_link(key, nodeid):
-            setup_virtual_link(slice, key, rate, myid, nodeid, ipaddr)
+            if netns:
+                setup_virtual_link(slice, key, rate, myid, nodeid, 
+                                   ipaddr, myvirtip, virtnet)
         else:
         else:
-            logger.log("Virtual link to node %s exists" % nodeid)
+            logger.log("%s: virtual link to node %s exists" % (slice, nodeid))
+            refresh_virtual_link(nodeid, key)
 
 
-    clean_up_old_virtual_links(slice, key, nodelist)
+    if not nat_exists(key):
+        if netns:
+            setup_nat(slice, myid, key)
+    else:
+        logger.log("%s: NAT exists" % slice)
+        refresh_nat(key)
+
+
+"""
+Write /etc/vservers/<slicename>/spaces/net
+"""
+def writeConf(slicename, value):
+    SLICEDIR="/etc/vservers/%s/" % slicename
+    SPACESDIR="%s/spaces/" % SLICEDIR
+    if os.path.exists(SLICEDIR):
+        if not os.path.exists(SPACESDIR):
+            try:
+                os.mkdir(SPACESDIR)
+            except os.error:
+                logger.log("topo: could not create %s\n" % SPACESDIR)
+                return
+        f = open("%s/net" % SPACESDIR, "w")
+        f.write("%s\n" % value)
+        f.close()
+        STATUS="OFF"
+        if value:
+            STATUS="ON"
+        logger.log("%s: network namespace %s\n" % (slicename, STATUS))
+
+
+"""
+Generate information for each interface in the sliver, in order to configure
+Quagga.
+"""
+def get_ifaces(hostname, myid, topospec, key):
+    ifaces = {}
+    topolist = convert_topospec_to_list(topospec)
+    for (nodeid, ipaddr, rate, myvirtip, remvirtip, virtnet) in topolist:
+        name = "a%sx%s" % (key, nodeid)
+        ifaces[name] = {}
+        ifaces[name]['remote-ip'] = remvirtip
+        ifaces[name]['local-ip'] = myvirtip
+        ifaces[name]['network'] = virtnet
+        ifaces[name]['short-name'] = hostname.replace('.vini-veritas.net', '')
+    return ifaces
+
+
+def write_header(f, myname, password):
+    f.write ("""! Configuration for %s
+! Generated at %s
+
+hostname %s
+password %s
+
+""" % (myname, strftime("%Y-%m-%d %H:%M:%S"), myname, password))
+    return
+
+
+"""
+IP address of NAT gateway to outside world
+"""
+def nat_gw(key):
+    return "10.0.%s.1" %  key
+
+"""
+IP address of the NAT interface inside the slice
+"""
+def nat_inner(key):
+    return "10.0.%s.2" % key
+
+
+"""
+Write zebra.conf file for Quagga
+"""
+def write_zebra(filename, myname, ifaces, myid, key):
+    f = open(filename, 'w')
+    password = "zebra"
+    write_header(f, myname, password)
+
+    f.write ("enable password %s\n" % password)
+
+    for name in ifaces:
+        f.write ("""!     
+interface %s
+link-detect
+""" %  name)
+
+    f.write ("""!
+access-list vty permit 127.0.0.1/32
+!
+line vty
+!
+""")
+    f.close()
+    return
+
+
+"""
+Write ospfd.conf file for Quagga.  
+"""
+def write_ospf(filename, myname, ifaces):
+    f = open(filename, 'w')
+    password = "zebra"
+    write_header(f, myname, password)
+
+    for name in ifaces:
+        f.write ("""!
+     interface %s
+     ip ospf cost 10
+     ip ospf hello-interval 5
+     ip ospf dead-interval 10
+     ip ospf network non-broadcast
+""" % name)
+
+    f.write ("""!
+     router ospf
+     ospf router-id %s
+""" % ifaces[name]['local-ip'])
+
+    for name in ifaces:
+        f.write ("     neighbor %s\n" % ifaces[name]['remote-ip'])
+
+    for name in ifaces:
+        net = ifaces[name]['network']
+        f.write ("     network %s area 0\n" % net)
+
+    f.write("""     redistribute kernel
+!
+access-list vty permit 127.0.0.1/32
+!
+line vty
+""")
+    return
+
+
+"""
+Write config files directly into the slice's file system.
+"""
+def update_quagga_configs(slicename, hostname, myid, topo, key, netns):
+    ifaces = get_ifaces(hostname, myid, topo, key)
+
+    quagga_dir = "/vservers/%s/etc/quagga/" % slicename
+    if not os.path.exists(quagga_dir):
+        try:
+            # Quagga not installed.  Install it here?  Chkconfig, sym links.
+            os.mkdir(quagga_dir)
+        except os.error:
+            logger.log("topo: could not create %s\n" % quagga_dir)
+            return
+
+    write_zebra(quagga_dir + "zebra.conf.generated", hostname, ifaces, 
+                myid, key)
+    write_ospf(quagga_dir + "ospfd.conf.generated", hostname, ifaces)
+
+    # Start up Quagga if we installed it earlier and netns = 1.
+
+    return
+
+
+"""
+Write /etc/hosts in the sliver
+"""
+def update_hosts(slicename, hosts):
+    hosts_file = "/vservers/%s/etc/hosts" % slicename
+    f = open(hosts_file, 'w')
+    f.write(hosts)
+    f.close()
+    return
 
 
 def start(options, config):
 
 
 def start(options, config):
+    run ("echo 1 > /proc/sys/net/ipv4/ip_forward")
     pass
 
 
 """
 Update the virtual links for a sliver if it has a 'netns' attribute,
 an 'egre_key' attribute, and a 'topo_rspec' attribute.
     pass
 
 
 """
 Update the virtual links for a sliver if it has a 'netns' attribute,
 an 'egre_key' attribute, and a 'topo_rspec' attribute.
+
+Creating the virtual link depends on the contents of 
+/etc/vservers/<slice>/spaces/net.  Update this first.
 """
 def GetSlivers(data):
 """
 def GetSlivers(data):
-    global ifaces
-    ifaces = sioc.gifconf()
+    global ifaces, old_ifaces
+    ifaces = old_ifaces = sioc.gifconf()
 
     for sliver in data['slivers']:
         attrs = {}
         for attribute in sliver['attributes']:
             attrs[attribute['name']] = attribute['value']
 
     for sliver in data['slivers']:
         attrs = {}
         for attribute in sliver['attributes']:
             attrs[attribute['name']] = attribute['value']
-        if 'netns' in attrs and 'egre_key' in attrs and 'topo_rspec' in attrs:
-            if attrs['netns'] > 0:
-                logger.log("Update topology for slice %s" % sliver['name'])
-                update(sliver['name'], data['node_id'], 
-                       attrs['topo_rspec'], attrs['egre_key'])
+        if 'netns' in attrs:
+            netns = int(attrs['netns'])
+            writeConf(sliver['name'], netns)
+        else:
+            netns = 0
+
+        if vserver.VServer(sliver['name']).is_running():
+            if 'egre_key' in attrs and 'topo_rspec' in attrs:
+                logger.log("topo: Update topology for slice %s" % \
+                               sliver['name'])
+                update_links(sliver['name'], data['node_id'], 
+                             attrs['topo_rspec'], attrs['egre_key'], netns)
+                update_quagga_configs(sliver['name'], data['hostname'],
+                               data['node_id'], attrs['topo_rspec'], 
+                               attrs['egre_key'], netns)
+            if 'hosts' in attrs:
+                update_hosts(sliver['name'], attrs['hosts'])
+        else:
+            logger.log("topo: sliver %s not running yet. Deferring." % \
+                           sliver['name'])
+
+    clean_up_old_virtual_links()
+    return
+