Don't advertise peer nodes in the RSpec
[sfa.git] / sfa / plc / network.py
index a653e6e..c6dad6e 100644 (file)
@@ -123,7 +123,7 @@ class Slice:
     """
     Use with tags that can have more than one instance
     """
-    def get_tags(self, tagname, node = None):
+    def get_multi_tag(self, tagname, node = None):
         tags = []
         for i in self.slice_tag_ids:
             tag = self.network.lookupSliceTag(i)
@@ -157,7 +157,7 @@ class Slice:
         else:
             record['node_id'] = None
         tag = Slicetag(record)
-        self.network.slicetags[tag.id] = tag
+        self.network.tags[tag.id] = tag
         self.slice_tag_ids.append(tag.id)
         tag.changed = True       
         tag.updated = True
@@ -175,15 +175,29 @@ class Slice:
             tag = self.add_tag(tagname, value, node)
         tag.updated = True
             
-    def tags_to_xml(self, xml, node = None):
-        tag = self.get_tag("cpu_pct", node)
-        if tag:
-            with xml.cpu_percent:
-                xml << tag.value
-        tags = self.get_tags("vsys", node)
+    def update_multi_tag(self, tagname, value, node = None):
+        tags = self.get_multi_tag(tagname, node)
         for tag in tags:
-            with xml.vsys:
-                xml << tag.value
+            if tag and tag.value == value:
+                value = "no change"
+                break
+        else:
+            tag = self.add_tag(tagname, value, node)
+        tag.updated = True
+            
+    def tags_to_xml(self, xml, node = None):
+        tagtypes = self.network.getTagTypes()
+        for tt in tagtypes:
+            if tt.multi:
+                tags = self.get_multi_tag(tt.tagname, node)
+                for tag in tags:
+                    if not tag.deleted:  ### Debugging
+                        xml << (tag.tagname, tag.value)
+            else:
+                tag = self.get_tag(tt.tagname, node)
+                if tag:
+                    if not tag.deleted:   ### Debugging
+                        xml << (tag.tagname, tag.value)
 
     def toxml(self, xml):
         with xml.sliver_defaults:
@@ -222,6 +236,16 @@ class Slicetag:
             api.plshell.DeleteSliceTag(api.plauth, self.id)
 
 
+class TagType:
+    def __init__(self, tagtype):
+        self.id = tagtype['tag_type_id']
+        self.tagname = tagtype['tagname']
+        if self.tagname in ['codemux', 'ip_addresses', 'vsys']:
+            self.multi = True
+        else:
+            self.multi = False
+
+
 """
 A Network is a compound object consisting of:
 * a dictionary mapping site IDs to Site objects
@@ -236,6 +260,7 @@ class Network:
         self.nodes = self.get_nodes(api)
         self.ifaces = self.get_ifaces(api)
         self.tags = self.get_slice_tags(api)
+        self.tagtypes = self.get_tag_types(api)
         self.slice = None
     
     """ Lookup site based on id or idtag value """
@@ -246,7 +271,7 @@ class Network:
         try:
             val = self.sites[id]
         except:
-            raise KeyError("site ID %s not found" % id)
+            raise InvalidRSpec("site ID %s not found" % id)
         return val
     
     def getSites(self):
@@ -263,7 +288,7 @@ class Network:
         try:
             val = self.nodes[id]
         except:
-            raise KeyError("node ID %s not found" % id)
+            raise InvalidRSpec("node ID %s not found" % id)
         return val
     
     def getNodes(self):
@@ -280,7 +305,7 @@ class Network:
         try:
             val = self.ifaces[id]
         except:
-            raise KeyError("interface ID %s not found" % id)
+            raise InvalidRSpec("interface ID %s not found" % id)
         return val
     
     def getIfaces(self):
@@ -302,7 +327,7 @@ class Network:
         try:
             val = self.tags[id]
         except:
-            raise KeyError("slicetag ID %s not found" % id)
+            raise InvalidRSpec("slicetag ID %s not found" % id)
         return val
     
     def getSliceTags(self):
@@ -311,21 +336,49 @@ class Network:
             tags.append(self.tags[t])
         return tags
     
+    def lookupTagType(self, name):
+        val = None
+        try:
+            val = self.tagstypes[name]
+        except:
+            raise InvalidRSpec("tag %s not found" % name)
+        return val
+    
+    def getTagTypes(self):
+        tags = []
+        for t in self.tagtypes:
+            tags.append(self.tagtypes[t])
+        return tags
+    
+    def __process_attributes(self, element, node=None):
+        # Do we need to check caller's role before update???
+        tagtypes = self.getTagTypes()
+        for tt in tagtypes:
+            if tt.multi:
+                for e in element.iterfind("./" + tt.tagname):
+                    self.slice.update_multi_tag(tt.tagname, e.text, node)
+            else:
+                e = element.find("./" + tt.tagname)
+                if e is not None:
+                    self.slice.update_tag(tt.tagname, e.text, node)
+
     """
     Annotate the objects in the Network with information from the RSpec
     """
-    def addRSpec(self, xml):
+    def addRSpec(self, xml, schema=None):
         nodedict = {}
         for node in self.getNodes():
             nodedict[node.idtag] = node
             
-        slicenodes = {}
-
-        tree = etree.parse(StringIO(xml))
+        try:
+            tree = etree.parse(StringIO(xml))
+        except etree.XMLSyntaxError:
+            message = str(sys.exc_info()[1])
+            raise InvalidRSpec(message)
 
-        if self.schema:
+        if schema:
             # Validate the incoming request against the RelaxNG schema
-            relaxng_doc = etree.parse(self.schema)
+            relaxng_doc = etree.parse(schema)
             relaxng = etree.RelaxNG(relaxng_doc)
         
             if not relaxng(tree):
@@ -335,18 +388,21 @@ class Network:
 
         rspec = tree.getroot()
 
+        defaults = rspec.find("./network/sliver_defaults")
+        self.__process_attributes(defaults)
+
         # 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()
+            self.__process_attributes(sliver, node)
 
         # 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()
+            self.__process_attributes(sliver, node)
 
         return
 
@@ -356,7 +412,7 @@ class Network:
     def addSlice(self):
         slice = self.slice
         if not slice:
-            raise Error("no slice associated with network")
+            raise InvalidRSpec("no slice associated with network")
 
         for node in slice.get_nodes():
             node.add_sliver()
@@ -364,13 +420,13 @@ class Network:
     """
     Write any slice tags that have been added or modified back to the DB
     """
-    def updateSliceTags(self, slice):
+    def updateSliceTags(self):
         # Update slice tags in database
         for tag in self.getSliceTags():
-            if tag.slice_id == slice.id:
-                if tag.tagname == 'topo_rspec' and not tag.updated:
+            if tag.slice_id == self.slice.id:
+                if not tag.updated:
                     tag.delete()
-                tag.write(self.api)
+                #tag.write(self.api)  ### Debugging
 
     """
     Produce XML directly from the topology specification.
@@ -398,7 +454,7 @@ class Network:
     """
     def get_sites(self, api):
         tmp = []
-        for site in api.plshell.GetSites(api.plauth):
+        for site in api.plshell.GetSites(api.plauth, {'peer_id': None}):
             t = site['site_id'], Site(self, site)
             tmp.append(t)
         return dict(tmp)
@@ -409,7 +465,7 @@ class Network:
     """
     def get_nodes(self, api):
         tmp = []
-        for node in api.plshell.GetNodes(api.plauth):
+        for node in api.plshell.GetNodes(api.plauth, {'peer_id': None}):
             t = node['node_id'], Node(self, node)
             tmp.append(t)
         return dict(tmp)
@@ -434,6 +490,17 @@ class Network:
             tmp.append(t)
         return dict(tmp)
     
+    """
+    Create a list of tagtype obects keyed by tag name
+    """
+    def get_tag_types(self, api):
+        tmp = []
+        for tag in api.plshell.GetTagTypes(api.plauth):
+            if tag['category'] == 'slice/rspec':
+                t = tag['tagname'], TagType(tag)
+                tmp.append(t)
+        return dict(tmp)
+    
     """
     Return a Slice object for a single slice
     """