Rewrite to make easier to read, maintain
Andy Bavier [Tue, 26 May 2009 18:03:53 +0000 (18:03 +0000)]
create-topo-attributes.py

index f03bebe..02bed13 100755 (executable)
@@ -10,166 +10,264 @@ import string
 import socket
 from topology import links
 
+class Node:
+    def __init__(self, node):
+        self.id = node['node_id']
+        self.hostname = node['hostname']
+        self.shortname = self.hostname.replace('.vini-veritas.net', '')
+        self.site_id = node['site_id']
+        self.ipaddr = socket.gethostbyname(self.hostname)
 
-def get_adjacency_matrix(links):
-    topo = {}
-    for (a, b) in links:
-        aNodes = get_sitenodes(a)
-        bNodes = get_sitenodes(b)
-        for nodeA in aNodes:
-            for nodeB in bNodes:
-                if nodeA not in topo:
-                    topo[nodeA] = {}
-                if nodeB not in topo:
-                    topo[nodeB] = {}
-                topo[nodeA][nodeB] = 1
-                topo[nodeB][nodeA] = 1
-    return topo
-                    
-            
-
-def get_site(nodeid):
-    if nodes[nodeid]:
-        return nodes[nodeid]['site_id']
-    raise Exception("Nodeid %s not found." % nodeid)
-
-
-def get_ipaddr(nodeid):
-    if nodes[nodeid]:
-        return socket.gethostbyname(nodes[nodeid]['hostname'])
-    raise Exception("Nodeid %s not found." % nodeid)
+    def get_link_id(self, remote):
+        if self.id < remote.id:
+            link = (self.id<<7) + remote.id
+        else:
+            link = (remote.id<<7) + self.id
+        return link
+        
+    def get_iface_id(self, remote):
+        if self.id < remote.id:
+            iface = 1
+        else:
+            iface = 2
+        return iface
+    
+    def get_virt_ip(self, remote):
+        link = self.get_link_id(remote)
+        iface = self.get_iface_id(remote)
+        first = link >> 6
+        second = ((link & 0x3f)<<2) + iface
+        return "192.168.%d.%d" % (first, second)
 
+    def get_virt_net(self, remote):
+        link = self.get_link_id(remote)
+        first = link >> 6
+        second = (link & 0x3f)<<2
+        return "192.168.%d.%d/30" % (first, second)
+        
+    def get_site(self, sites):
+        return sites[self.site_id]
+            
+    
+    # What nodes in the set of node_ids are adjacent to this one?
+    def adjacent_nodes(self, sites, nodes, node_ids):
+        mysite = self.get_site(sites)
+        adj_ids = mysite.adj_node_ids.intersection(node_ids)
+        adj_nodes = []
+        for id in adj_ids:
+            adj_nodes.append(nodes[id])
+        return adj_nodes
+    
+    def init_rspecs(self):
+        self.rspecs = []
+        
+    def add_rspec(self, remote):
+        my_ip = self.get_virt_ip(remote)
+        remote_ip = remote.get_virt_ip(self)
+        net = self.get_virt_net(remote)
+        rspec = remote.id, remote.ipaddr, "1Mbit", my_ip, remote_ip, net
+        self.rspecs.append(rspec)
 
-def get_sitenodes(siteid):
-    if sites[siteid]:
-        return sites[siteid]['node_ids']
-    raise Exception("Siteid %s not found." % siteid)
+        
+class Site:
+    def __init__(self, site):
+        self.id = site['site_id']
+        self.node_ids = site['node_ids']
+        self.adj_site_ids = set()
+        self.adj_node_ids = set()
 
+    def get_sitenodes(self, nodes):
+        n = []
+        for i in self.node_ids:
+            n.append(nodes[i])
+        return n
+    
+    def add_adjacency(self, site):
+        self.adj_site_ids.add(site.id)
+        for n in site.node_ids:
+            self.adj_node_ids.add(n)
+        
+    
+class Slice:
+    def __init__(self, slice):
+        self.id = slice['slice_id']
+        self.name = slice['name']
+        self.node_ids = set(slice['node_ids'])
+        self.slice_tag_ids = slice['slice_tag_ids']
+        self.attrs = {}
+    
+    def get_tag(self, tagname, slicetags):
+        if tagname not in self.attrs:
+            for i in self.slice_tag_ids:
+                if slicetags[i].tagname == tagname:
+                    self.attrs[tagname] = slicetags[i]
+                    break
+            else:
+                self.attrs[tagname] = None
+        return self.attrs[tagname]
+        
+    def get_nodes(self, nodes):
+        n = []
+        for id in self.node_ids:
+            n.append(nodes[id])
+        return n
+    
+    def get_rspec(self, slicetags, node):
+        for i in self.slice_tag_ids:
+            tag = slicetags[i]
+            if tag.tagname == 'topo_rspec' and node.id == tag.node_id:
+                return tag
+        else:
+            return None
+                
 
-"""
-Find the IP address assigned to a virtual interface in the topology
-(for creating /etc/hosts).
-Each virtual link is on a /30 subnet.
-"""
-def get_linkid(myid, remoteid):
-    if myid < remoteid:
-        linkid = (myid<<7) + remoteid
-    else:
-        linkid = (remoteid<<7) + myid
-    return linkid
 
-def get_nodeid(myid, remoteid):
-    if myid < remoteid:
-        nodeid = 1
-    else:
-        nodeid = 2
-    return nodeid
-
-def get_virt_ip(myid, remoteid):
-    linkid = get_linkid(myid, remoteid)
-    nodeid = get_nodeid(myid, remoteid)
-    first = linkid >> 6
-    second = ((linkid & 0x3f)<<2) + nodeid
-    return "192.168.%d.%d" % (first, second)
-
-def get_virt_net(myid, remoteid):
-    linkid = get_linkid(myid, remoteid)
-    first = linkid >> 6
-    second = (linkid & 0x3f)<<2
-    return "192.168.%d.%d/30" % (first, second)
+class Slicetag:
+    def __init__(self, tag):
+        self.id = tag['slice_tag_id']
+        self.slice_id = tag['slice_id']
+        self.tagname = tag['tagname']
+        self.value = tag['value']
+        self.node_id = tag['node_id']
+        self.updated = 0
+      
+        
+                 
 
 """
-Create a dictionary of site records keyed by site ID
+Create a dictionary of site objects keyed by site ID
 """
 def get_sites():
     tmp = []
     for site in GetSites():
-        t = site['site_id'], site
+        t = site['site_id'], Site(site)
         tmp.append(t)
     return dict(tmp)
 
 
 """
-Create a dictionary of node records keyed by node ID
+Create a dictionary of node objects keyed by node ID
 """
 def get_nodes():
     tmp = []
     for node in GetNodes():
-        t = node['node_id'], node
+        t = node['node_id'], Node(node)
         tmp.append(t)
     return dict(tmp)
 
+"""
+Create a dictionary of slice objects keyed by slice ID
+"""
+def get_slices():
+    tmp = []
+    for slice in GetSlices():
+        t = slice['slice_id'], Slice(slice)
+        tmp.append(t)
+    return dict(tmp)
 
+"""
+Create a dictionary of slicetag objects keyed by slice tag ID
+"""
+def get_slice_tags():
+    tmp = []
+    for tag in GetSliceTags():
+        t = tag['slice_tag_id'], Slicetag(tag)
+        tmp.append(t)
+    return dict(tmp)
+    
+    
 # For debugging
 dryrun = 0
 
-""" Need global topology information """
 sites = get_sites()
 nodes = get_nodes()
+slices = get_slices()
+slicetags = get_slice_tags()
 
-adj_matrix = get_adjacency_matrix(links)
+# Add adjacencies
+for (a, b) in links:
+    sites[a].add_adjacency(sites[b])
+    sites[b].add_adjacency(sites[a])  
 
-for slice in GetSlices():
-    # Create dictionary of the slice's tags 
-    attrs ={}
-    topo_attr = {}
-    for tag in GetSliceTags(slice['slice_tag_ids']):
-        attrs[tag['tagname']] = tag['slice_tag_id']
-        if tag['tagname'] == 'topo_rspec' and tag['node_id']:
-            topo_attr[tag['node_id']] = tag['slice_tag_id']
-            
-    if dryrun and slice['name'] == 'pl_trellis':
-        attrs['egre_key'] = 101
+for i in slices:
+    slice = slices[i]
+    tag = slice.get_tag('vini_topo', slicetags)
+    if tag:
+        topo_type = tag.value
+    else:
+        topo_type = None
+    
+    if topo_type == 'vsys' or topo_type == 'iias':
+        """ 
+        Assign EGRE key to the slice if needed
+        If no 'netns' attribute, add netns/1
+        For 'vsys', add vsys/setup-link and vsys/setup-nat
+        """
         
-    if 'egre_key' in attrs:
-        #print "Virtual topology for %s:" % slice['name']
-        slicenodes = set(slice['node_ids'])
+    if slice.get_tag('egre_key', slicetags) and topo_type == 'iias':
+        if dryrun:
+            print "Virtual topology for %s:" % slice.name
+            
         hosts = "127.0.0.1\t\tlocalhost\n"
         """
-        For each node in the slice, check whether there are any adjacent
-        nodes also in the sliceset using the adjacency matrix.
-        For each pair of adjacent nodes, add to nodes' rspecs.
+        For each node in the slice, check whether the slice is running on any
+        adjacent nodes.  For each pair of adjacent nodes, add to nodes' rspecs.
         """
-        topo = {}
-        for a in slicenodes:
-            for b in adj_matrix[a]:
-                if b in slicenodes:
-                    if a not in topo:
-                        topo[a] = []
-                    my_ip = get_virt_ip(a, b)
-                    remote_ip = get_virt_ip(b, a)
-                    net = get_virt_net(a, b)
-                    link = b, get_ipaddr(b), "1Mbit", my_ip, remote_ip, net
-                    topo[a].append(link)
-                    shortname = nodes[a]['hostname'].replace('.vini-veritas.net', '')
-                    hosts += "%s\t\t%s\n" % (my_ip, shortname)
-
-        for node in topo:
-            topo_str = "%s" % topo[node]
-            if dryrun:
-                print node, topo_str
-            elif node in topo_attr:
-                UpdateSliceTag(topo_attr[node], topo_str)
-                del topo_attr[node]
-            else:
-                id = slice['slice_id']
-                AddSliceTag(id, 'topo_rspec', topo_str, node)
+        for node in slice.get_nodes(nodes):
+            node.init_rspecs()
+            adj_nodes = node.adjacent_nodes(sites, nodes, slice.node_ids)
+            for adj in adj_nodes:
+                node.add_rspec(adj)
+                hosts += "%s\t\t%s\n" % (node.get_virt_ip(adj), node.shortname)
+                
+            old_rspec = slice.get_rspec(slicetags, node)
+            if node.rspecs:
+                topo_str = "%s" % node.rspecs
 
+                if old_rspec:
+                    old_rspec.updated = 1
+                    id = old_rspec.id
+                    
+                if old_rspec and old_rspec.value == topo_str:
+                        topo_str = "no change"
+                elif not dryrun:
+                    if old_rspec:
+                        UpdateSliceTag(old_rspec.id, topo_str)
+                    else:
+                        AddSliceTag(slice.id, 'topo_rspec', topo_str, node.id)
+                        
+                if dryrun:
+                    if not id:
+                        id = 'new'
+                    print "[%s] rspec for %s: %s" % (id, node.shortname, topo_str)
+                    
+        hosttag = slice.get_tag('hosts', slicetags)
+        if hosttag:
+            hosttag.updated = 1
+        if hosttag and hosts == hosttag.value:
+            hosts = "/etc/hosts: no change"
+        elif not dryrun:
+            if hosttag:
+                UpdateSliceTag(hosttag.id, hosts)
+            else:
+                AddSliceTag(slice.id, 'hosts', hosts)
+            
         if dryrun:
             print hosts
-        elif 'hosts' in attrs:
-            UpdateSliceTag(attrs['hosts'], hosts)
-        else:
-            id = slice['slice_id']
-            AddSliceTag(id, 'hosts', hosts)
+
     else:
         if dryrun:
-            print "No EGRE key for %s" % slice['name']
+            print "Slice %s not using IIAS" % slice.name
 
-    # Remove old topo_rspec entries
-    
-    if not dryrun:
-        for node in topo_attr:
-            DeleteSliceTag(topo_attr[node])
+# Remove old topo_rspec entries
+for i in slicetags:
+    tag = slicetags[i]
+    if (tag.tagname == 'topo_rspec' or tag.tagname == 'hosts') and not tag.updated:
+        if dryrun:
+            slice = slices[tag.slice_id].name
+            node = nodes[tag.node_id].hostname
+            print "Deleting topo_rspec tag %s (%s, %s)" % (tag.id, slice, node)
+        else:
+            DeleteSliceTag(tag.id)