- RefreshPeer & AddSliceToNodes had bugs
[plcapi.git] / PLC / Peers.py
index c887702..872a867 100644 (file)
@@ -8,8 +8,11 @@ from types import StringTypes
 
 from PLC.Faults import *
 from PLC.Parameter import Parameter
+from PLC.Filter import Filter
 from PLC.Table import Row, Table
 
+from PLC.ForeignNodes import ForeignNodes,ForeignNode
+
 class Peer (Row):
     """
     Stores the list of peering PLCs in the peers table. 
@@ -35,6 +38,99 @@ class Peer (Row):
            raise invalid_url
        return url
 
+    def manage_node (self, foreign_node, add_if_true_del_if_false=True, commit=True):
+        """
+        Add foreign node to a peer
+        """
+
+        assert 'peer_id' in self
+        assert 'node_id' in foreign_node
+
+        peer_id = self['peer_id']
+        node_id = foreign_node ['node_id']
+
+        if add_if_true_del_if_false:
+            ### ADDING
+            sql = "INSERT INTO peer_node VALUES (%d,%d)" % (peer_id,node_id)
+            self.api.db.do(sql)
+            if self['node_ids'] is None:
+                self['node_ids']=[node_id,]
+            self['node_ids'].append(node_id)
+            ### DELETING
+        else:
+            sql = "DELETE FROM peer_node WHERE peer_id=%d AND node_id=%d" % (peer_id,node_id)
+            self.api.db.do(sql)
+            self['node_ids'].remove(node_id)
+
+        if commit:
+            self.api.db.commit()
+
+    def refresh_nodes (self, current_peer_nodes):
+        """
+        refreshes the foreign_nodes and peer_node tables
+        expected input is the current list of nodes as returned by GetNodes
+
+        returns the number of new nodes on this peer (can be negative)
+        """
+
+        peer_id = self['peer_id']
+        
+       # we get the whole table just in case 
+       # a host would have switched from one plc to the other
+       local_foreign_nodes = ForeignNodes (self.api)
+        # new to index it by hostname for searching later
+        local_foreign_nodes.hostname_index()
+       
+       ### mark entries for this peer outofdate
+        old_count=0;
+       for foreign_node in local_foreign_nodes:
+           if foreign_node['peer_id'] == peer_id:
+               foreign_node.uptodate=False
+                old_count += 1
+
+        ### these fields get copied through
+        ### xxx need to figure how to revert unix timestamp to db timestamp format
+        remote_fields = ['boot_state','model','version','date_created','last_updated']
+
+        
+       ### scan the new entries, and mark them uptodate
+       for node in current_peer_nodes:
+           hostname = node['hostname']
+            try:
+                foreign_node = local_foreign_nodes.hostname_locate(hostname)
+                if foreign_node['peer_id'] != peer_id:
+                    ### the node has changed its plc, needs to update peer_node
+                    old_peer_id = foreign_node['peer_id']
+                    old_peers=Peers(self.api,[peer_id])
+                    assert old_peer[0]
+                    old_peers[0].manage_node(foreign_node,False)
+                    self.manage_node(foreign_node,True)
+                    foreign_node['peer_id'] = peer_id
+               ### update it anyway: copy other relevant fields
+                for field in remote_fields:
+                    foreign_node[field]=node[field]
+                # this row is now valid
+                foreign_node.uptodate=True
+                foreign_node.sync()
+           except:
+                new_foreign_node = ForeignNode(self.api, {'hostname':hostname})
+                for field in remote_fields:
+                    new_foreign_node[field]=node[field]
+                ### need to sync so we get a node_id
+                new_foreign_node.sync()
+                new_foreign_node.uptodate = True
+                self.manage_node(new_foreign_node,True)
+                local_foreign_nodes.hostname_add_by(new_foreign_node)
+
+
+       ### delete entries that are not uptodate
+        for foreign_node in local_foreign_nodes:
+            if not foreign_node.uptodate:
+                foreign_node.delete()
+
+        return len(current_peer_nodes)-old_count
+        
+        
     def delete (self, commit=True):
        """
        Delete peer
@@ -50,29 +146,21 @@ class Peers (Table):
     Maps to the peers table in the database
     """
     
-    def __init__ (self, api, peer_id_or_peername_list = None):
-       self.api = api
-
-       sql="SELECT %s FROM view_peers WHERE deleted IS False" % \
-           ", ".join(Peer.fields)
-       if peer_id_or_peername_list:
-            peer_ids = [x for x in peer_id_or_peername_list if isinstance(x, (int, long))]
-            peernames = [x for x in peer_id_or_peername_list if isinstance(x, StringTypes)]
-           sql += " AND (False"
-           if peer_ids:
-               sql += " OR peer_id in (%s)"% ", ".join([str(i) for i in peer_ids])
-           if peernames:
-               sql += " OR peername in (%s)"% ". ".join(api.db.quote(peernames)).lower()
-           sql += ")"
-
-       rows = self.api.db.selectall(sql)
-
-       for row in rows:
-           self[row['peer_id']] = peer = Peer(api,row)
-            for aggregate in ['node_ids']:
-                if not peer.has_key(aggregate) or peer[aggregate] is None:
-                    peer[aggregate] = []
-                else:
-                    peer[aggregate] = map(int, peer[aggregate].split(','))
+    def __init__ (self, api, peer_filter = None, columns = None):
+        Table.__init__(self, api, Peer, columns)
+
+       sql = "SELECT %s FROM view_peers WHERE deleted IS False" % \
+              ", ".join(self.columns)
 
+        if peer_filter is not None:
+            if isinstance(peer_filter, (list, tuple, set)):
+                # Separate the list into integers and strings
+                ints = filter(lambda x: isinstance(x, (int, long)), peer_filter)
+                strs = filter(lambda x: isinstance(x, StringTypes), peer_filter)
+                peer_filter = Filter(Peer.fields, {'peer_id': ints, 'peername': strs})
+                sql += " AND (%s)" % peer_filter.sql(api, "OR")
+            elif isinstance(peer_filter, dict):
+                peer_filter = Filter(Peer.fields, peer_filter)
+                sql += " AND (%s)" % peer_filter.sql(api, "AND")
 
+       self.selectall(sql)