(no commit message)
[sfa.git] / sfa / plc / network.py
index 7f92d73..16a4467 100644 (file)
@@ -28,6 +28,7 @@ class Iface:
         self.ipv4 = iface['ip']
         self.bwlimit = iface['bwlimit']
         self.hostname = iface['hostname']
+        self.primary = iface['is_primary']
 
     """
     Just print out bwlimit right now
@@ -39,7 +40,7 @@ class Iface:
 
 
 class Node:
-    def __init__(self, network, node, bps = 1000 * 1000000):
+    def __init__(self, network, node):
         self.network = network
         self.id = node['node_id']
         self.idtag = "n%s" % self.id
@@ -49,13 +50,12 @@ class Node:
         self.sliver = None
         self.whitelist = node['slice_ids_whitelist']
 
-    def get_ifaces(self):
-        i = []
+    def get_primary_iface(self):
         for id in self.iface_ids:
-            i.append(self.network.lookupIface(id))
-            # Only return the first interface
-            break
-        return i
+            iface = self.network.lookupIface(id)
+            if iface.primary:
+                return iface
+        return None
         
     def get_site(self):
         return self.network.lookupSite(self.site_id)
@@ -72,10 +72,8 @@ class Node:
         with xml.node(id = self.idtag):
             with xml.hostname:
                 xml << self.hostname
-            if self.network.type == "VINI":
-                with xml.kbps:
-                    xml << str(int(self.bps/1000))
-            for iface in self.get_ifaces():
+            iface = self.get_primary_iface()
+            if iface:
                 iface.toxml(xml)
             if self.sliver:
                 self.sliver.toxml(xml)
@@ -150,8 +148,9 @@ class Slice:
         return n
   
     # Add a new slice tag   
-    def add_tag(self, tagname, value, node = None):
-        record = {'slice_tag_id':None, 'slice_id':self.id, 'tagname':tagname, 'value':value}
+    def add_tag(self, tagname, value, node = None, category = 'slice/rspec'):
+        record = {'slice_tag_id':None, 'slice_id':self.id, 'tagname':tagname, 'value':value, 
+                  'category':category}
         if node:
             record['node_id'] = node.id
         else:
@@ -161,6 +160,7 @@ class Slice:
         self.slice_tag_ids.append(tag.id)
         tag.changed = True       
         tag.updated = True
+        tag.writable = True
         return tag
     
     # Update a slice tag if it exists, else add it             
@@ -174,6 +174,8 @@ class Slice:
         else:
             tag = self.add_tag(tagname, value, node)
         tag.updated = True
+        tag.writable = True
+        return tag
             
     def update_multi_tag(self, tagname, value, node = None):
         tags = self.get_multi_tag(tagname, node)
@@ -184,16 +186,23 @@ class Slice:
         else:
             tag = self.add_tag(tagname, value, node)
         tag.updated = True
+        tag.writable = True
+        return tag
             
     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_multi_tag("vsys", node)
-        for tag in tags:
-            with xml.vsys:
-                xml << tag.value
+        tagtypes = self.network.getTagTypes()
+        for tt in tagtypes:
+            if tt.in_rspec:
+                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:
@@ -212,16 +221,23 @@ class Slicetag:
         self.tagname = tag['tagname']
         self.value = tag['value']
         self.node_id = tag['node_id']
+        self.category = tag['category']
         self.updated = False
         self.changed = False
         self.deleted = False
-    
+        self.writable = False
+        if self.category == 'slice/rspec':
+            self.writable = True
+        
     # Mark a tag as deleted
     def delete(self):
         self.deleted = True
         self.updated = True
     
     def write(self, api):
+        if not self.writable:
+            return
+
         if self.changed:
             if int(self.id) > 0:
                 api.plshell.UpdateSliceTag(api.plauth, self.id, self.value)
@@ -232,6 +248,19 @@ class Slicetag:
             api.plshell.DeleteSliceTag(api.plauth, self.id)
 
 
+class TagType:
+    def __init__(self, tagtype):
+        self.id = tagtype['tag_type_id']
+        self.category = tagtype['category']
+        self.tagname = tagtype['tagname']
+        self.multi = False
+        self.in_rspec = False
+        if self.category == 'slice/rspec':
+            self.in_rspec = True
+        if self.tagname in ['codemux', 'ip_addresses', 'vsys']:
+            self.multi = True
+
+
 """
 A Network is a compound object consisting of:
 * a dictionary mapping site IDs to Site objects
@@ -246,6 +275,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 """
@@ -256,7 +286,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):
@@ -273,7 +303,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):
@@ -290,7 +320,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):
@@ -312,7 +342,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):
@@ -321,23 +351,45 @@ 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):
-        for e in element.iterfind("./vsys"):
-            self.slice.update_multi_tag("vsys", e.text, node)
-            
+        if element is None:
+            return 
+
+        # Do we need to check caller's role before update???
+        tagtypes = self.getTagTypes()
+        for tt in tagtypes:
+            if tt.in_rspec:
+                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, 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 schema:
             # Validate the incoming request against the RelaxNG schema
@@ -350,6 +402,7 @@ class Network:
                 raise InvalidRSpec(message)
 
         rspec = tree.getroot()
+        self.rspec = rspec
 
         defaults = rspec.find("./network/sliver_defaults")
         self.__process_attributes(defaults)
@@ -357,17 +410,25 @@ class Network:
         # 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)
+            try:
+                node = self.lookupNode(elem.get("id"))
+            except:
+                # Don't worry about nodes from other aggregates
+                pass
+            else:
+                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)
+            try:
+                node = self.lookupNode(sliver.get("nodeid"))
+            except:
+                # Don't worry about nodes from other aggregates
+                pass
+            else:
+                node.add_sliver()
+                self.__process_attributes(sliver, node)
 
         return
 
@@ -377,7 +438,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()
@@ -391,7 +452,7 @@ class Network:
             if tag.slice_id == self.slice.id:
                 if not tag.updated:
                     tag.delete()
-                #tag.write(self.api)
+                tag.write(self.api) 
 
     """
     Produce XML directly from the topology specification.
@@ -419,7 +480,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)
@@ -430,7 +491,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)
@@ -455,6 +516,16 @@ 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):
+            t = tag['tagname'], TagType(tag)
+            tmp.append(t)
+        return dict(tmp)
+    
     """
     Return a Slice object for a single slice
     """