Generate Capacity in VINI Rspec
authorAndy Bavier <acb@cs.princeton.edu>
Wed, 19 Aug 2009 19:33:46 +0000 (19:33 +0000)
committerAndy Bavier <acb@cs.princeton.edu>
Wed, 19 Aug 2009 19:33:46 +0000 (19:33 +0000)
sfa/rspecs/aggregates/rspec_manager_vini.py
sfa/rspecs/aggregates/vini/__init__.py [new file with mode: 0644]
sfa/rspecs/aggregates/vini/rspec.py [new file with mode: 0644]
sfa/rspecs/aggregates/vini/topology.py [new file with mode: 0755]
sfa/rspecs/aggregates/vini/utils.py [moved from sfa/rspecs/aggregates/vini_utils.py with 86% similarity]
sfa/rspecs/aggregates/vini/vini.xml [moved from sfa/rspecs/aggregates/vini.xml with 100% similarity]

index d2d5e60..f84f5c1 100644 (file)
@@ -3,10 +3,10 @@ from sfa.util.misc import *
 from sfa.util.rspec import Rspec
 from sfa.server.registry import Registries
 from sfa.plc.nodes import *
-from sfa.rspecs.aggregates.vini_utils import *
+from sfa.rspecs.aggregates.vini.utils import *
+from sfa.rspecs.aggregates.vini.rspec import *
 import sys
 
-SFA_VINI_DEFAULT_RSPEC = '/etc/sfa/vini.rspec'
 SFA_VINI_WHITELIST = '/etc/sfa/vini.whitelist'
 
 """
@@ -134,62 +134,54 @@ def create_slice_vini_aggregate(api, hrn, nodes):
     return 1
 
 def get_rspec(api, hrn):
-    # Get default rspec
-    default = Rspec()
-    default.parseFile(SFA_VINI_DEFAULT_RSPEC)
+    rspec = ViniRspec()  
+    (sites, nodes, tags) = get_topology(api)  
+
+    rspec.updateCapacity(sites, nodes)
     
     if (hrn):
         slicename = hrn_to_pl_slicename(hrn)
-        defaultrspec = default.toDict()
-        nodedict = get_nodedict(defaultrspec)
-
-        # call the default sfa.plc.nodes.get_rspec() method
-        nodes = Nodes(api)     
-        rspec = nodes.get_rspec(hrn)     
-
-        # Grab all the PLC info we'll need at once
         slice = get_slice(api, slicename)
         if slice:
-            nodes = get_nodes(api)
-            tags = get_slice_tags(api)
-
-            # Add the node tags from the Capacity statement to Node objects
-            for (k, v) in nodedict.iteritems():
-                for id in nodes:
-                    if v == nodes[id].hostname:
-                        nodes[id].tag = k
-
-            endpoints = []
-            for node in slice.get_nodes(nodes):
-                linktag = slice.get_tag('topo_rspec', tags, node)
-                if linktag:
-                    l = eval(linktag.value)
-                    for (id, realip, bw, lvip, rvip, vnet) in l:
-                        endpoints.append((node.id, id, bw))
-            
-            if endpoints:
-                linkspecs = []
-                for (l, r, bw) in endpoints:
-                    if (r, l, bw) in endpoints:
-                        if l < r:
-                            edict = {}
-                            edict['endpoint'] = [nodes[l].tag, nodes[r].tag]
-                            edict['bw'] = [bw]
-                            linkspecs.append(edict)
-
-                d = default.toDict()
-                d['Rspec']['Request'][0]['NetSpec'][0]['LinkSpec'] = linkspecs
-                d['Rspec']['Request'][0]['NetSpec'][0]['name'] = hrn
-                new = Rspec()
-                new.parseDict(d)
-                rspec = new.toxml()
-    else:
-        # Return canned response for now...
-        rspec = default.toxml()
+            slice.hrn = hrn
+            rspec.updateRequest(slice, nodes, tags)
+        else:
+            # call the default sfa.plc.nodes.get_rspec() method
+            return Nodes(api).get_rspec(hrn)     
 
-    return rspec
+    return rspec.toxml()
+
+
+"""
+Check the requested topology against the available topology and capacity
+"""
+def check_request(hrn, rspec, nodes, sites, sitelinks, maxbw):
+    linkspecs = rspec['Rspec']['Request'][0]['NetSpec'][0]['LinkSpec']
+    if linkspecs:
+        for l in linkspecs:
+            n1 = Node.lookup(l['endpoint'][0])
+            n2 = Node.lookup(l['endpoint'][1])
+            bw = l['bw'][0]
+            reqbps = get_tc_rate(bw)
+            maxbps = get_tc_rate(maxbw)
 
+            if reqbps <= 0:
+                raise GeniInvalidArgument(bw, "BW")
+            if reqbps > maxbps:
+                raise PermissionError(" %s requested %s but max BW is %s" % 
+                                      (hrn, bw, maxbw))
+
+            if adjacent_nodes(n1, n2, sites, sitelinks):
+                availbps = get_avail_bps(n1, n2, sites, sitelinks)
+                if availbps < reqbps:
+                    raise PermissionError("%s: capacity exceeded" % hrn)
+            else:
+                raise PermissionError("%s: nodes %s and %s not adjacent" 
+                                      % (hrn, n1.tag, n2.tag))
 
+"""
+Hook called via 'sfi.py create'
+"""
 def create_slice(api, hrn, xml):
     r = Rspec(xml)
     rspec = r.toDict()
@@ -203,24 +195,15 @@ def create_slice(api, hrn, xml):
         whitelist[slice] = maxbw
         
     if hrn in whitelist:
-        maxbps = get_tc_rate(whitelist[hrn])
+        maxbw = whitelist[hrn]
     else:
         raise PermissionError("%s not in VINI whitelist" % hrn)
         
-    ### Check to make sure that the slice isn't requesting more
-    ### than its maximum bandwidth.
-    linkspecs = rspec['Rspec']['Request'][0]['NetSpec'][0]['LinkSpec']
-    if linkspecs:
-        for l in linkspecs:
-            bw = l['bw'][0]
-            bps = get_tc_rate(bw)
-            if bps <= 0:
-                raise GeniInvalidArgument(bw, "BW")
-            if bps > maxbps:
-                raise PermissionError(" %s requested %s but max BW is %s" % (hrn, bw, whitelist[hrn]))
+    # Construct picture of global topology
+    (sites, nodes, tags) = get_topology(api)
 
     # Check request against current allocations
-    # Request OK
+    #check_request(hrn, rspec, nodes, sites, sitelinks, maxbw)
 
     nodes = rspec_to_nodeset(rspec)
     create_slice_vini_aggregate(api, hrn, nodes)
@@ -230,13 +213,8 @@ def create_slice(api, hrn, xml):
         linkspecs = rspec['Rspec']['Request'][0]['NetSpec'][0]['LinkSpec']
         if linkspecs:
             slicename = hrn_to_pl_slicename(hrn)
-
-            # Grab all the PLC info we'll need at once
             slice = get_slice(api, slicename)
             if slice:
-                nodes = get_nodes(api)
-                tags = get_slice_tags(api)
-
                 slice.update_tag('vini_topo', 'manual', tags)
                 slice.assign_egre_key(tags)
                 slice.turn_on_netns(tags)
diff --git a/sfa/rspecs/aggregates/vini/__init__.py b/sfa/rspecs/aggregates/vini/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/sfa/rspecs/aggregates/vini/rspec.py b/sfa/rspecs/aggregates/vini/rspec.py
new file mode 100644 (file)
index 0000000..3076219
--- /dev/null
@@ -0,0 +1,68 @@
+from sfa.util.rspec import Rspec
+from sfa.rspecs.aggregates.vini.utils import *
+import sys
+
+SFA_VINI_DEFAULT_RSPEC = '/etc/sfa/vini.rspec'
+
+class ViniRspec(Rspec):
+    def __init__(self):
+        Rspec.__init__(self)
+        self.parseFile(SFA_VINI_DEFAULT_RSPEC)
+        
+    def updateCapacity(self, sites, nodes):
+        d = self.toDict()
+        sitespecs = []
+        sitelinkspecs = []
+        for s in sites:
+            site = sites[s]
+            if not site.public:
+                continue
+            sdict = {}
+            nodespecs = []
+            for node in site.get_sitenodes(nodes):
+                if not node.tag:
+                    continue
+                ndict = {}
+                ndict['hostname'] = [node.hostname]
+                ndict['name'] = node.tag
+                ndict['bw'] = ['999Mbit']
+                nodespecs.append(ndict)
+            sdict['NodeSpec'] = nodespecs
+            sdict['name'] = site.name
+            sitespecs.append(sdict)
+            
+            for sl in site.sitelinks:
+                if sl.site1 == site:
+                    sldict = {}
+                    sldict['endpoint'] = [sl.site1.name, sl.site2.name]
+                    sldict['bw'] = [str(sl.availMbps) + "Mbit"]
+                    sitelinkspecs.append(sldict)
+                    
+        d['Rspec']['Capacity'][0]['NetSpec'][0]['SiteSpec'] = sitespecs
+        d['Rspec']['Capacity'][0]['NetSpec'][0]['SiteLinkSpec'] = sitelinkspecs
+        self.parseDict(d)
+
+
+    def updateRequest(self, slice, nodes, tags):
+        endpoints = []
+        for node in slice.get_nodes(nodes):
+            linktag = slice.get_tag('topo_rspec', tags, node)
+            if linktag:
+                l = eval(linktag.value)
+                for (id, realip, bw, lvip, rvip, vnet) in l:
+                    endpoints.append((node.id, id, bw))
+            
+        if endpoints:
+            linkspecs = []
+            for (l, r, bw) in endpoints:
+                if (r, l, bw) in endpoints:
+                    if l < r:
+                        edict = {}
+                        edict['endpoint'] = [nodes[l].tag, nodes[r].tag]
+                        edict['bw'] = [bw]
+                        linkspecs.append(edict)
+
+            d = self.toDict()
+            d['Rspec']['Request'][0]['NetSpec'][0]['LinkSpec'] = linkspecs
+            d['Rspec']['Request'][0]['NetSpec'][0]['name'] = slice.hrn
+            self.parseDict(d)
\ No newline at end of file
diff --git a/sfa/rspecs/aggregates/vini/topology.py b/sfa/rspecs/aggregates/vini/topology.py
new file mode 100755 (executable)
index 0000000..cb65fb5
--- /dev/null
@@ -0,0 +1,38 @@
+#!/usr/bin/python
+
+# $Id: topology.py 14181 2009-07-01 19:46:07Z acb $
+# $URL: https://svn.planet-lab.org/svn/NodeManager-topo/trunk/topology.py $
+
+#
+# Links in the physical topology, gleaned from looking at the Internet2
+# and NLR topology maps.  Link (a, b) connects sites with IDs a and b.
+#
+PhysicalLinks = [(2, 12),  # I2 Princeton - New York 
+         (4, 5),   # NLR Chicago - Houston
+         (4, 6),   # NLR Chicago - Atlanta
+         (4, 7),   # NLR Chicago - Seattle
+         (4, 9),   # NLR Chicago - New York
+         (4, 10),  # NLR Chicago - Wash DC
+         (5, 6),   # NLR Houston - Atlanta
+         (5, 8),   # NLR Houston - Los Angeles
+         (6, 10),  # NLR Atlanta - Wash DC
+         (6, 14),  # NLR Atlanta - Ga Tech
+         (7, 8),   # NLR Seattle - Los Angeles
+         (9, 10),  # NLR New York - Wash DC
+         (11, 13), # I2 Chicago - Wash DC
+         (11, 15), # I2 Chicago - Atlanta
+         (11, 16), # I2 Chicago - CESNET
+         (11, 17), # I2 Chicago - Kansas City
+         (12, 13), # I2 New York - Wash DC
+         (13, 15), # I2 Wash DC - Atlanta
+         (14, 15), # Ga Tech - I2 Atlanta
+         (15, 19), # I2 Atlanta - Houston
+         (17, 19), # I2 Kansas City - Houston
+         (17, 22), # I2 Kansas City - Salt Lake City
+         (17, 24), # I2 Kansas City - UMKC
+         (19, 20), # I2 Houston - Los Angeles
+         (20, 21), # I2 Los Angeles - Seattle
+         (20, 22), # I2 Los Angeles - Salt Lake City
+         (21, 22)] # I2 Seattle - Salt Lake City
+
+
similarity index 86%
rename from sfa/rspecs/aggregates/vini_utils.py
rename to sfa/rspecs/aggregates/vini/utils.py
index 82f8606..8250e84 100644 (file)
@@ -1,5 +1,6 @@
 import re
 import socket
+from sfa.rspecs.aggregates.vini.topology import *
 
 # Taken from bwlimit.py
 #
@@ -86,14 +87,6 @@ class Node:
         
     def get_site(self, sites):
         return sites[self.site_id]
-            
-    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_links(self):
         self.links = []
@@ -104,14 +97,36 @@ class Node:
         net = self.get_virt_net(remote)
         link = remote.id, remote.ipaddr, bw, my_ip, remote_ip, net
         self.links.append(link)
+        
+    def add_tag(self, sites):
+        s = self.get_site(sites)
+        words = self.hostname.split(".")
+        index = words[0].replace("node", "")
+        if index.isdigit():
+            self.tag = s.tag + index
+        else:
+            self.tag = None
+        
 
+class SiteLink:
+    def __init__(self, site1, site2, mbps = 1000):
+        self.site1 = site1
+        self.site2 = site2
+        self.totalMbps = mbps
+        self.availMbps = mbps
+        
+        site1.add_sitelink(self)
+        site2.add_sitelink(self)
+        
         
 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()
+        self.name = site['abbreviated_name']
+        self.tag = site['login_base']
+        self.public = site['is_public']
+        self.sitelinks = []
 
     def get_sitenodes(self, nodes):
         n = []
@@ -119,11 +134,9 @@ class Site:
             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)
-        
+    def add_sitelink(self, link):
+        self.sitelinks.append(link)
+    
     
 class Slice:
     def __init__(self, slice):
@@ -277,9 +290,9 @@ class Slicetag:
 """
 Create a dictionary of site objects keyed by site ID
 """
-def get_sites():
+def get_sites(api):
     tmp = []
-    for site in GetSites():
+    for site in api.plshell.GetSites(api.plauth):
         t = site['site_id'], Site(site)
         tmp.append(t)
     return dict(tmp)
@@ -334,4 +347,23 @@ def free_egre_key(slicetags):
         
     return "%s" % key
    
+"""
+Return the network topology.
+The topology consists of:
+* a dictionary mapping site IDs to Site objects
+* a dictionary mapping node IDs to Node objects
+* the Site objects are connected via SiteLink objects representing
+  the physical topology and available bandwidth
+"""
+def get_topology(api):
+    sites = get_sites(api)
+    nodes = get_nodes(api)
+    tags = get_slice_tags(api)
+    
+    for (s1, s2) in PhysicalLinks:
+        SiteLink(sites[s1], sites[s2])
 
+    for id in nodes:
+        nodes[id].add_tag(sites)
+        
+    return (sites, nodes, tags)