Generate Capacity in VINI Rspec
[sfa.git] / sfa / rspecs / aggregates / rspec_manager_vini.py
index dd68a6e..f84f5c1 100644 (file)
@@ -3,10 +3,11 @@ 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.rspec import *
 import sys
-import pdb
 
-SFA_VINI_DEFAULT_RSPEC = '/etc/sfa/vini.rspec'
+SFA_VINI_WHITELIST = '/etc/sfa/vini.whitelist'
 
 """
 Copied from create_slice_aggregate() in sfa.plc.slices
@@ -112,70 +113,166 @@ def create_slice_vini_aggregate(api, hrn, nodes):
     # add nodes from rspec
     added_nodes = list(set(nodes).difference(hostnames))
 
+    """
+    print >> sys.stderr, "Slice on nodes:"
+    for n in hostnames:
+        print >> sys.stderr, n
+    print >> sys.stderr, "Wants nodes:"
+    for n in nodes:
+        print >> sys.stderr, n
+    print >> sys.stderr, "Deleting nodes:"
+    for n in deleted_nodes:
+        print >> sys.stderr, n
+    print >> sys.stderr, "Adding nodes:"
+    for n in added_nodes:
+        print >> sys.stderr, n
+    """
+
     api.plshell.AddSliceToNodes(api.plauth, slicename, added_nodes) 
     api.plshell.DeleteSliceFromNodes(api.plauth, slicename, deleted_nodes)
 
     return 1
 
 def get_rspec(api, hrn):
-    rspec = None
+    rspec = ViniRspec()  
+    (sites, nodes, tags) = get_topology(api)  
+
+    rspec.updateCapacity(sites, nodes)
     
     if (hrn):
-        # XX rspec is expected to be xml, not None.
-        # call the default sfa.plc.nodes.get_rspec() methods
-        # until things are complete here
-        nodes = Nodes(api)     
-        rspec = nodes.get_rspec(hrn)     
-               
-        # Convert HRN to slice name
-        # Get SliceTags for the slice
-
-        # Construct LinkSpecs from the topo_rspec SliceTags
-        # The first field is the NodeId of the remote node.
-        # So the endpoints are the SliceTag node and the remote node.
-
-        # How to:
-        # - avoid duplicates?
-        # - verify both ends of the link?
-        pass
-    else:
-        # Return canned response for now...
-        r = Rspec()
-        r.parseFile(SFA_VINI_DEFAULT_RSPEC)
-        rspec = r.toxml()
+        slicename = hrn_to_pl_slicename(hrn)
+        slice = get_slice(api, slicename)
+        if slice:
+            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()
-    r.parseString(xml)
-    rspec = r.toGenDict()
+    r = Rspec(xml)
+    rspec = r.toDict()
+
+    ### Check the whitelist
+    ### It consists of lines of the form: <slice hrn> <bw>
+    whitelist = {}
+    f = open(SFA_VINI_WHITELIST)
+    for line in f.readlines():
+        (slice, maxbw) = line.split()
+        whitelist[slice] = maxbw
+        
+    if hrn in whitelist:
+        maxbw = whitelist[hrn]
+    else:
+        raise PermissionError("%s not in VINI 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)
 
     # Add VINI-specific topology attributes to slice here
+    try:
+        linkspecs = rspec['Rspec']['Request'][0]['NetSpec'][0]['LinkSpec']
+        if linkspecs:
+            slicename = hrn_to_pl_slicename(hrn)
+            slice = get_slice(api, slicename)
+            if slice:
+                slice.update_tag('vini_topo', 'manual', tags)
+                slice.assign_egre_key(tags)
+                slice.turn_on_netns(tags)
+                slice.add_cap_net_admin(tags)
+
+                nodedict = {}
+                for (k, v) in get_nodedict(rspec).iteritems():
+                    for id in nodes:
+                        if v == nodes[id].hostname:
+                            nodedict[k] = nodes[id]
+
+                for l in linkspecs:
+                    n1 = nodedict[l['endpoint'][0]]
+                    n2 = nodedict[l['endpoint'][1]]
+                    bw = l['bw'][0]
+                    n1.add_link(n2, bw)
+                    n2.add_link(n1, bw)
+
+                for node in slice.get_nodes(nodes):
+                    if node.links:
+                        topo_str = "%s" % node.links
+                        slice.update_tag('topo_rspec', topo_str, tags, node)
+
+                # Update slice tags in database
+                for i in tags:
+                    tag = tags[i]
+                    if tag.slice_id == slice.id:
+                        if tag.tagname == 'topo_rspec' and not tag.updated:
+                            tag.delete()
+                        tag.write(api)
+    except KeyError:
+        # Bad Rspec
+        pass
+    
 
     return True
 
-def rspec_to_nodeset(rspec):
+def get_nodedict(rspec):
     nodedict = {}
-    nodes = set()
-    try:
-        sitespecs = rspec['Rspec'][0]['Capacity'][0]['NetSpec'][0]['SiteSpec']
+    try:    
+        sitespecs = rspec['Rspec']['Capacity'][0]['NetSpec'][0]['SiteSpec']
         for s in sitespecs:
             for node in s['NodeSpec']:
-                nodedict[node['name'][0]] = node['hostname'][0]
+                nodedict[node['name']] = node['hostname'][0]
+    except KeyError:
+        pass
+
+    return nodedict
 
-        linkspecs = rspec['Rspec'][0]['Request'][0]['NetSpec'][0]['LinkSpec']
+       
+def rspec_to_nodeset(rspec):
+    nodes = set()
+    try:
+        nodedict = get_nodedict(rspec)
+        linkspecs = rspec['Rspec']['Request'][0]['NetSpec'][0]['LinkSpec']
         for l in linkspecs:
             for e in l['endpoint']:
                 nodes.add(nodedict[e])
-        
     except KeyError:
         # Bad Rspec
         pass
@@ -185,7 +282,7 @@ def rspec_to_nodeset(rspec):
 def main():
     r = Rspec()
     r.parseFile(sys.argv[1])
-    rspec = r.toGenDict()
+    rspec = r.toDict()
     create_slice(None,'plc',rspec)
     
 if __name__ == "__main__":