Use lxml.etree to parse VINI RSpec, validate against RelaxNG schema
authorAndy Bavier <acb@cs.princeton.edu>
Thu, 28 Jan 2010 15:38:01 +0000 (15:38 +0000)
committerAndy Bavier <acb@cs.princeton.edu>
Thu, 28 Jan 2010 15:38:01 +0000 (15:38 +0000)
sfa/managers/aggregate_manager_vini.py
sfa/rspecs/aggregates/vini/utils.py
sfa/util/faults.py

index e1e7a17..418f034 100644 (file)
@@ -1,11 +1,9 @@
 from sfa.util.faults import *
 from sfa.util.namespace import *
-from sfa.util.rspec import RSpec
 from sfa.server.registry import Registries
 from sfa.plc.nodes import *
 from sfa.plc.api import *
 from sfa.rspecs.aggregates.vini.utils import *
-from sfa.rspecs.aggregates.vini.rspec import *
 import sys
 
 """
@@ -153,10 +151,8 @@ Hook called via 'sfi.py create'
 """
 def create_slice(api, xrn, xml):
     hrn = urn_to_hrn(xrn)[0]
-    rspec = RSpec(xml)
     topo = Topology(api)
-    
-    topo.nodeTopoFromRSpec(rspec)
+    topo.nodeTopoFromRSpec(xml)
 
     # Check request against current allocations
     topo.verifyNodeTopo(hrn, topo)
index c9ca7da..e2fa953 100644 (file)
@@ -4,7 +4,11 @@ import socket
 from sfa.util.faults import *
 from sfa.rspecs.aggregates.vini.topology import *
 from xmlbuilder import XMLBuilder
+from lxml import etree
 import sys
+from StringIO import StringIO
+
+VINI_RELAXNG_SCHEMA = "/var/www/html/schemas/vini.rng"
 
 # Taken from bwlimit.py
 #
@@ -481,18 +485,20 @@ class Topology:
 
     def __add_vlink(self, vlink, slicenodes, parent = None):
         n1 = n2 = None
-        if 'endpoints' in vlink:
-            end = vlink['endpoints'].split()
-            n1 = self.lookupNode(end[0])
-            n2 = self.lookupNode(end[1])
+        endpoints = vlink.get("endpoints")
+        if endpoints:
+            (end1, end2) = endpoints.split()
+            n1 = self.lookupNode(end1)
+            n2 = self.lookupNode(end2)
         elif parent:
-            """ Try to infer the endpoints """
-            (n1, n2) = self.__infer_endpoints(parent['endpoints'],slicenodes)
+            """ Try to infer the endpoints for the virtual link """
+            site_endpoints = parent.get("endpoints")
+            (n1, n2) = self.__infer_endpoints(site_endpoints, slicenodes)
         else:
             raise Error("no endpoints given")
 
         #print "Added virtual link: %s -- %s" % (n1.tag, n2.tag)
-        bps = int(vlink['kbps'][0]) * 1000
+        bps = int(vlink.findtext("kbps")) * 1000
         sitelink = self.lookupSiteLink(n1, n2)
         if not sitelink:
             raise PermissionError("nodes %s and %s not adjacent" % 
@@ -520,38 +526,59 @@ class Topology:
         #print "Inferred endpoints: %s %s" % (n[0].idtag, n[1].idtag)
         return n
         
-    def nodeTopoFromRSpec(self, rspec):
+    def nodeTopoFromRSpec(self, xml):
         if self.nodelinks:
             raise Error("virtual topology already present")
             
-        rspecdict = rspec.toDict()
         nodedict = {}
         for node in self.getNodes():
             nodedict[node.idtag] = node
             
         slicenodes = {}
 
-        top = rspecdict['RSpec']
-        if ('network' in top):
-            sites = top['network'][0]['site']
-            for site in sites:
-                for node in site['node']:
-                    if 'sliver' in node:
-                        n = nodedict[node['id']]
-                        slicenodes[n.id] = n
-                        n.add_sliver()
-            links = top['network'][0]['link']
-            for link in links:
-                if 'vlink' in link:
-                    for vlink in link['vlink']:
-                        self.__add_vlink(vlink, slicenodes, link)
-        elif ('request' in top):
-            for sliver in top['request'][0]['sliver']:
-                n = nodedict[sliver['nodeid']]
-                slicenodes[n.id] = n
-                n.add_sliver()
-            for vlink in top['request'][0]['vlink']:
-                self.__add_vlink(vlink, slicenodes)
+        tree = etree.parse(StringIO(xml))
+
+        # Validate the incoming request against the RelaxNG schema
+        relaxng_doc = etree.parse(VINI_RELAXNG_SCHEMA)
+        relaxng = etree.RelaxNG(relaxng_doc)
+        
+        if not relaxng(tree):
+            error = relaxng.error_log.last_error
+            message = "%s (line %s)" % (error.message, error.line)
+            raise InvalidRSpec(message)
+
+        rspec = tree.getroot()
+
+        """
+        Handle requests where the user has annotated a description of the
+        physical resources (nodes and links) with virtual ones (slivers
+        and vlinks).
+        """
+        # Find slivers under node elements
+        for sliver in rspec.iterfind("./network/site/node/sliver"):
+            elem = sliver.getparent()
+            node = nodedict[elem.get("id")]
+            slicenodes[node.id] = node
+            node.add_sliver()
+
+        # Find links under link elements
+        for vlink in rspec.iterfind("./network/link/vlink"):
+            link = vlink.getparent()
+            self.__add_vlink(vlink, slicenodes, link)
+
+        """
+        Handle requests where the user has listed the virtual resources only
+        """
+        # Find slivers that specify nodeid
+        for sliver in rspec.iterfind("./request/sliver[@nodeid]"):
+            node = nodedict[sliver.get("nodeid")]
+            slicenodes[node.id] = node
+            node.add_sliver()
+
+        # Find vlinks that specify endpoints
+        for vlink in rspec.iterfind("./request/vlink[@endpoints]"):
+            self.__add_vlink(vlink, slicenodes)
+
         return
 
     def nodeTopoFromSliceTags(self, slice):
index 5a32885..d1f070c 100644 (file)
@@ -255,3 +255,11 @@ class MissingSfaInfo(SfaFault):
     def __str__(self):
         return repr(self.value)
 
+class InvalidRSpec(SfaFault):
+    def __init__(self, value, extra = None):
+        self.value = value
+        faultString = "Invalid RSpec: %(value)s" % locals()
+        SfaFault.__init__(self, 108, faultString, extra)
+    def __str__(self):
+        return repr(self.value)
+