federation in progress - associate a local slice to a foreign node
authorThierry Parmentelat <thierry.parmentelat@sophia.inria.fr>
Wed, 8 Nov 2006 17:34:07 +0000 (17:34 +0000)
committerThierry Parmentelat <thierry.parmentelat@sophia.inria.fr>
Wed, 8 Nov 2006 17:34:07 +0000 (17:34 +0000)
PLC/ForeignNodes.py
PLC/Methods/AddSliceToNodes.py
PLC/Methods/GetForeignNodes.py
PLC/Methods/RefreshPeer.py [new file with mode: 0644]
PLC/Methods/UpdatePeer.py [deleted file]
PLC/Nodes.py
PLC/Peers.py
PLC/Slices.py
planetlab4.sql

index 0a135ea..9bc0c63 100644 (file)
@@ -13,20 +13,33 @@ class ForeignNode (Row) :
     other peering instances of myplc
     """
 
-    table_name = 'foreign_nodes'
-    primary_key = 'foreign_node_id'
+    table_name = 'nodes'
+    primary_key = 'node_id'
 
     fields = {
-       'foreign_node_id': Parameter (int, "Foreign Node Id"),
+       'node_id': Parameter (int, "Node Id"),
        'hostname': Parameter (str, "Node name"),
-       'boot_state' : Parameter (str, "Boot state"),
        'peer_id': Parameter (str, "Peer id"),
+       'boot_state' : Parameter (str, "Boot state, see Node"),
+        'model' : Parameter (str,"Model, see Node"),
+        'version' : Parameter (str,"Version, see Node"),
+        'date_created': Parameter(int, "Creation time, see Node"),
+        'last_updated': Parameter(int, "Update time, see Node"),
        }
 
     def __init__(self,api,fields={},uptodate=True):
        Row.__init__(self,api,fields)
        self.uptodate=uptodate
 
+    def delete (self, commit=True):
+        """
+        Delete existing foreign node.
+        """
+        print 'in ForeignNode::delete',self
+        self['deleted']=True
+        self.sync(commit)
+        
+
 class ForeignNodes (Table):
 
     def __init__ (self, api, foreign_node_id_or_hostname_list=None):
@@ -34,8 +47,8 @@ class ForeignNodes (Table):
        self.api=api
 
        sql =""
-       sql += "SELECT %s FROM foreign_nodes " % ", ".join(ForeignNode.fields)
-       sql += "WHERE foreign_nodes.deleted IS False " 
+       sql += "SELECT %s FROM view_foreign_nodes " % ", ".join(ForeignNode.fields)
+       sql += "WHERE view_foreign_nodes.deleted IS False " 
 
        if foreign_node_id_or_hostname_list:
            foreign_node_id_list = [ str(x) for x in foreign_node_id_or_hostname_list 
@@ -44,7 +57,7 @@ class ForeignNodes (Table):
                              if isinstance(x, StringTypes)]
            sql += " AND (False"
            if foreign_node_id_list:
-               sql += " OR foreign_node_id in (%s)" % ", ".join(foreign_node_id_list)
+               sql += " OR node_id in (%s)" % ", ".join(foreign_node_id_list)
            if hostname_list:
                ## figure how to retrieve peer_id from the hostname(s)
                sql += " OR hostname IN (%s)" % ", ".join(api.db.quote(hostname_list))
index 3b194b4..37ad4a3 100644 (file)
@@ -2,12 +2,17 @@ from PLC.Faults import *
 from PLC.Method import Method
 from PLC.Parameter import Parameter, Mixed
 from PLC.Nodes import Node, Nodes
+from PLC.ForeignNodes import ForeignNode, ForeignNodes
 from PLC.Slices import Slice, Slices
 from PLC.Auth import Auth
 
 class AddSliceToNodes(Method):
     """
-    Adds the specified slice to the specified nodes. If the slice is
+    Adds the specified slice to the specified nodes.
+    Nodes can be either regular (local) nodes as returned by GetNodes
+    or foreign nodes as returned by GetForeignNodes
+
+    If the slice is
     already associated with a node, no errors are returned. 
 
     Returns 1 if successful, faults otherwise.
@@ -37,22 +42,28 @@ class AddSliceToNodes(Method):
 
         slice = slices.values()[0]
 
-        # Get specified nodes
-        nodes = Nodes(self.api, node_id_or_hostname_list).values()
-
         if 'admin' not in self.caller['roles']:
             if self.caller['person_id'] in slice['person_ids']:
                 pass
+            # Thierry : I cannot figure out how this works
+            # how is having pi role related to being in a slice ?
             elif 'pi' not in self.caller['roles']:
                 raise PLCPermissionDenied, "Not a member of the specified slice"
             elif slice['site_id'] not in self.caller['site_ids']:
                 raise PLCPermissionDenied, "Specified slice not associated with any of your sites"
        
-       # Add slice to all nodes found
+        # Get specified nodes, and them to the slice
+        nodes = Nodes(self.api, node_id_or_hostname_list).values()
        for node in nodes:
             if slice['slice_id'] not in node['slice_ids']:
                 slice.add_node(node, commit = False)
 
+        # the same for foreign_nodes
+        foreign_nodes = ForeignNodes (self.api, node_id_or_hostname_list).values()
+        for foreign_node in foreign_nodes:
+            if slice['slice_id'] not in foreign_node['slice_ids']:
+                slice.add_node (foreign_node, is_foreign_node=True, commit=False)
+
         slice.sync()
 
        self.object_ids = [node['node_id'] for node in nodes]
index 230240b..e9c2321 100644 (file)
@@ -17,7 +17,7 @@ class GetForeignNodes (Method):
     roles = ['admin']
 
     accepts = [ Auth(),
-               [ Mixed(ForeignNode.fields['foreign_node_id'],
+               [ Mixed(ForeignNode.fields['node_id'],
                        ForeignNode.fields['hostname'])]
                ]
     
diff --git a/PLC/Methods/RefreshPeer.py b/PLC/Methods/RefreshPeer.py
new file mode 100644 (file)
index 0000000..099b737
--- /dev/null
@@ -0,0 +1,103 @@
+#
+# Thierry Parmentelat - INRIA
+# 
+
+import xmlrpclib
+
+from PLC.Faults import *
+from PLC.Method import Method
+from PLC.Parameter import Parameter, Mixed
+from PLC.Auth import Auth
+
+from PLC.Peers import Peer, Peers
+from PLC.Persons import Person, Persons
+from PLC.ForeignNodes import ForeignNode, ForeignNodes
+
+
+class RefreshPeer(Method):
+    """
+    Query a peer PLC for its list of nodes, and refreshes
+    the local database accordingly
+    
+    Returns None
+    """
+    
+    roles = ['admin']
+    
+    accepts = [ Auth(),
+               Parameter (int, "Peer id") ]
+    
+    returns = None
+
+    def call (self, auth, peer_id):
+       
+       ### retrieve peer info
+       peers = Peers (self.api)
+       peer = peers[peer_id]
+       
+       ### retrieve account info
+       person_id = peer['person_id']
+       persons = Persons (self.api,[person_id])
+       person = persons[person_id]
+       
+       ### build up foreign auth
+       auth={ 'Username': person['email'],
+              'AuthMethod' : 'password',
+              'AuthString' : person['password'],
+              'Role' : 'admin' }
+
+       ## connect to the peer's API
+       apiserver = xmlrpclib.Server (peer['peer_url']+"/PLCAPI/")
+       print 'auth',auth
+       current_peer_nodes = apiserver.GetNodes(auth,[])
+       
+       ## manual feed for tests
+#      n1 = {'hostname': 'n1.plc', 'boot_state': 'inst'}
+#      n2 = {'hostname': 'n2.plc', 'boot_state': 'inst'}
+#      n3 = {'hostname': 'n3.plc', 'boot_state': 'inst'}
+        n11={'session': None, 'slice_ids': [], 'nodegroup_ids': [], 'last_updated': 1162884349, 'version': None, 'nodenetwork_ids': [], 'boot_state': 'inst', 'hostname': 'n11.plc1.org', 'site_id': 1, 'ports': None, 'pcu_ids': [], 'boot_nonce': None, 'node_id': 1, 'root_person_ids': [], 'key': None, 'date_created': 1162884349, 'model': None, 'conf_file_ids': [], 'ssh_rsa_key': None}
+        n12={'session': None, 'slice_ids': [], 'nodegroup_ids': [], 'last_updated': 1162884349, 'version': None, 'nodenetwork_ids': [], 'boot_state': 'inst', 'hostname': 'n12.plc1.org', 'site_id': 1, 'ports': None, 'pcu_ids': [], 'boot_nonce': None, 'node_id': 1, 'root_person_ids': [], 'key': None, 'date_created': 1162884349, 'model': None, 'conf_file_ids': [], 'ssh_rsa_key': None}
+        n21={'session': None, 'slice_ids': [], 'nodegroup_ids': [], 'last_updated': 1162884349, 'version': None, 'nodenetwork_ids': [], 'boot_state': 'boot', 'hostname': 'n21.plc2.org', 'site_id': 1, 'ports': None, 'pcu_ids': [], 'boot_nonce': None, 'node_id': 1, 'root_person_ids': [], 'key': None, 'date_created': 1162884349, 'model': None, 'conf_file_ids': [], 'ssh_rsa_key': None}
+        n22={'session': None, 'slice_ids': [], 'nodegroup_ids': [], 'last_updated': 1162884349, 'version': None, 'nodenetwork_ids': [], 'boot_state': 'boot', 'hostname': 'n22.plc2.org', 'site_id': 1, 'ports': None, 'pcu_ids': [], 'boot_nonce': None, 'node_id': 1, 'root_person_ids': [], 'key': None, 'date_created': 1162884349, 'model': None, 'conf_file_ids': [], 'ssh_rsa_key': None}
+
+#        current_peer_nodes = [n21,n22]
+
+       ### now to the db
+       # we get the whole table just in case 
+       # a host would have switched from one plc to the other
+       foreign_nodes = ForeignNodes (self.api)
+       
+       ### mark entries for this peer outofdate
+       for foreign_node in foreign_nodes.values():
+           if foreign_node['peer_id'] == peer_id:
+               foreign_node.uptodate=False
+
+        ### these fields get copied through
+        remote_fields = ['boot_state','model','version','date_created','date_updated']
+        
+       ### scan the new entries, and mark them uptodate
+       for node in current_peer_nodes:
+           hostname = node['hostname']
+           foreign_node = foreign_nodes.get(hostname)
+           if foreign_node:
+               ### update it anyway
+                foreign_node['cached'] = True
+               foreign_node['peer_id'] = peer_id
+                # copy other relevant fields
+                for field in remote_fields:
+                    foreign_node[field]=node[field]
+                # this row is valid
+               foreign_node.uptodate = True
+           else:
+               foreign_nodes[hostname] = ForeignNode(self.api,
+                                                     {'hostname':hostname,
+                                                       'cached':True,
+                                                      'peer_id':peer_id,})
+                for field in remote_fields:
+                    foreign_nodes[hostname][field]=node[field]
+                    
+           foreign_nodes[hostname].sync()
+
+       ### delete entries that are not uptodate
+       [ x.delete() for x in foreign_nodes.values() if not x.uptodate ]
+       
diff --git a/PLC/Methods/UpdatePeer.py b/PLC/Methods/UpdatePeer.py
deleted file mode 100644 (file)
index 1249a47..0000000
+++ /dev/null
@@ -1,88 +0,0 @@
-#
-# Thierry Parmentelat - INRIA
-# 
-
-import xmlrpclib
-
-from PLC.Faults import *
-from PLC.Method import Method
-from PLC.Parameter import Parameter, Mixed
-from PLC.Auth import Auth
-
-from PLC.Peers import Peer, Peers
-from PLC.Persons import Person, Persons
-from PLC.ForeignNodes import ForeignNode, ForeignNodes
-
-
-class UpdatePeer(Method):
-    """
-    Query a peer PLC for its list of nodes, and refreshes
-    the local database accordingly
-    
-    Returns None
-    """
-    
-    roles = ['admin']
-    
-    accepts = [ Auth(),
-               Parameter (int, "Peer id") ]
-    
-    returns = None
-
-    def call (self, auth, peer_id):
-       
-       ### retrieve peer info
-       peers = Peers (self.api)
-       peer = peers[peer_id]
-       
-       ### retrieve account info
-       person_id = peer['person_id']
-       persons = Persons (self.api,[person_id])
-       person = persons[person_id]
-       
-       ### build up foreign auth
-       auth={ 'Username': person['email'],
-              'AuthMethod' : 'password',
-              'AuthString' : person['password'],
-              'Role' : 'admin' }
-
-       ## connect to the peer's API
-       apiserver = xmlrpclib.Server (peer['peer_url']+"/PLCAPI/")
-       print 'auth',auth
-       current_peer_nodes = apiserver.GetNodes(auth,[])
-       
-       ## manual feed for tests
-#      n1 = {'hostname': 'n1.plc', 'boot_state': 'inst'}
-#      n2 = {'hostname': 'n2.plc', 'boot_state': 'inst'}
-#      n3 = {'hostname': 'n3.plc', 'boot_state': 'inst'}
-#      current_peer_nodes = [n2,n3]
-
-       ### now to the db
-       # we get the whole table just in case 
-       # a host would have switched from one plc to the other
-       foreign_nodes = ForeignNodes (self.api)
-       
-       ### mark entries for this peer outofdate
-       for foreign_node in foreign_nodes.values():
-           if foreign_node['peer_id'] == peer_id:
-               foreign_node.uptodate=False
-
-       ### scan the new entries, and mark them uptodate
-       for node in current_peer_nodes:
-           hostname = node['hostname']
-           foreign_node = foreign_nodes.get(hostname)
-           if foreign_node:
-               ### update it anyway
-               foreign_node['peer_id'] = peer_id
-               foreign_node['boot_state'] = node['boot_state']
-               foreign_node.uptodate = True
-           else:
-               foreign_nodes[hostname] = ForeignNode(self.api,
-                                                     {'hostname':hostname,
-                                                      'boot_state':node['boot_state'],
-                                                      'peer_id':peer_id})
-           foreign_nodes[hostname].sync()
-
-       ### delete entries that are not uptodate
-       [ x.delete() for x in foreign_nodes.values() if not x.uptodate ]
-       
index 16c63cb..a591601 100644 (file)
@@ -4,7 +4,7 @@
 # Mark Huang <mlhuang@cs.princeton.edu>
 # Copyright (C) 2006 The Trustees of Princeton University
 #
-# $Id: Nodes.py,v 1.15 2006/10/27 15:32:43 mlhuang Exp $
+# $Id: Nodes.py,v 1.16 2006/11/02 18:32:55 mlhuang Exp $
 #
 
 from types import StringTypes
@@ -107,8 +107,9 @@ class Nodes(Table):
     def __init__(self, api, node_id_or_hostname_list = None):
         self.api = api
 
-        sql = "SELECT %s FROM view_nodes WHERE deleted IS False" % \
-              ", ".join(Node.fields)
+        sql =  ""
+        sql += "SELECT %s FROM view_nodes " %  ", ".join(Node.fields)
+        sql += "WHERE deleted IS False"
 
         if node_id_or_hostname_list:
             # Separate the list into integers and strings
index 9686044..c887702 100644 (file)
@@ -22,8 +22,8 @@ class Peer (Row):
        'peer_id' : Parameter (int, "Peer identifier"),
        'peername' : Parameter (str, "Peer name"),
        'peer_url' : Parameter (str, "Peer API url"),
-       'person_id' : Parameter (int, "person_id of the account used to log there"),
-       'foreign_node_ids' : Parameter ([int], "doc")
+       'person_id' : Parameter (int, "Person_id of the account storing credentials - temporary"),
+       'node_ids' : Parameter ([int], "This peer's nodes ids")
        }
 
     def validate_peer_url (self, url):
@@ -69,7 +69,7 @@ class Peers (Table):
 
        for row in rows:
            self[row['peer_id']] = peer = Peer(api,row)
-            for aggregate in ['foreign_node_ids']:
+            for aggregate in ['node_ids']:
                 if not peer.has_key(aggregate) or peer[aggregate] is None:
                     peer[aggregate] = []
                 else:
index 8eec308..81df6c9 100644 (file)
@@ -8,6 +8,7 @@ from PLC.Debug import profile
 from PLC.Table import Row, Table
 from PLC.SliceInstantiations import SliceInstantiations
 from PLC.Nodes import Node, Nodes
+from PLC.ForeignNodes import ForeignNode, ForeignNodes
 import PLC.Persons
 
 class Slice(Row):
@@ -130,13 +131,16 @@ class Slice(Row):
             self['person_ids'].remove(person_id)
             person['slice_ids'].remove(slice_id)
 
-    def add_node(self, node, commit = True):
+    def add_node(self, node, is_foreign_node = False, commit = True):
         """
         Add node to existing slice.
         """
 
         assert 'slice_id' in self
-        assert isinstance(node, Node)
+        if not is_foreign_node:
+            assert isinstance(node, Node)
+        else:
+            assert isinstance(node, ForeignNode)
         assert 'node_id' in node
 
         slice_id = self['slice_id']
index 5d3e264..9e1d1d5 100644 (file)
@@ -9,7 +9,7 @@
 --
 -- Copyright (C) 2006 The Trustees of Princeton University
 --
--- $Id: planetlab4.sql,v 1.26 2006/11/03 16:05:20 mlhuang Exp $
+-- $Id: planetlab4.sql,v 1.27 2006/11/03 20:36:05 thierry Exp $
 --
 
 --------------------------------------------------------------------------------
@@ -219,6 +219,8 @@ INSERT INTO roles (role_id, name) VALUES (30, 'user');
 INSERT INTO roles (role_id, name) VALUES (40, 'tech');
 INSERT INTO roles (role_id, name) VALUES (1000, 'node');
 INSERT INTO roles (role_id, name) VALUES (2000, 'anonymous');
+-- xxx not sure this us useful yet
+--INSERT INTO roles (role_id, name) VALUES (3000, 'peer');
 
 CREATE TABLE person_role (
     person_id integer REFERENCES persons NOT NULL, -- Account identifier
@@ -251,13 +253,26 @@ INSERT INTO boot_states (boot_state) VALUES ('rins');
 INSERT INTO boot_states (boot_state) VALUES ('rcnf');
 INSERT INTO boot_states (boot_state) VALUES ('new');
 
+-- Peers
+CREATE TABLE peers (
+     peer_id  serial PRIMARY KEY, -- identifier
+     peername text NOT NULL,      -- free text
+     peer_url text NOT NULL,      -- the url of that peer's API
+     person_id integer REFERENCES persons NOT NULL, -- the account we use for logging in
+       
+     deleted boolean NOT NULL DEFAULT false
+) WITH OIDS;
+
+
 -- Nodes
 CREATE TABLE nodes (
     -- Mandatory
     node_id serial PRIMARY KEY, -- Node identifier
     hostname text NOT NULL, -- Node hostname
-    site_id integer REFERENCES sites NOT NULL, -- At which site
+    site_id integer REFERENCES sites, -- At which site (clause NOT NULL removed for foreign_nodes)
     boot_state text REFERENCES boot_states NOT NULL DEFAULT 'inst', -- Node boot state
+    cached boolean NOT NULL DEFAULT false,  -- is this entry cached from a peer ?
+    peer_id integer REFERENCES peers,      -- if cached, then from what peer
     deleted boolean NOT NULL DEFAULT false, -- Is deleted
 
     -- Optional
@@ -282,6 +297,20 @@ array_to_string(array_accum(node_id), ',') AS node_ids
 FROM nodes
 GROUP BY site_id;
 
+-- Nodes at each peer
+CREATE VIEW peer_nodes AS
+SELECT peer_id,
+array_to_string(array_accum(node_id), ',') AS node_ids
+FROM nodes 
+GROUP BY peer_id;
+
+CREATE VIEW view_peers AS
+SELECT 
+peers.*, 
+peer_nodes.node_ids 
+FROM peers
+LEFT JOIN peer_nodes USING (peer_id);
+
 --------------------------------------------------------------------------------
 -- Node groups
 --------------------------------------------------------------------------------
@@ -761,7 +790,6 @@ nodes.node_id,
 nodes.hostname,
 nodes.site_id,
 nodes.boot_state,
-nodes.deleted,
 nodes.model,
 nodes.boot_nonce,
 nodes.version,
@@ -775,14 +803,32 @@ node_slices.slice_ids,
 node_pcus.pcu_ids,
 node_pcus.ports,
 node_conf_files.conf_file_ids,
-node_session.session_id AS session
+node_session.session_id AS session,
+nodes.deleted
 FROM nodes
 LEFT JOIN node_nodenetworks USING (node_id)
 LEFT JOIN node_nodegroups USING (node_id)
 LEFT JOIN node_slices USING (node_id)
 LEFT JOIN node_pcus USING (node_id)
 LEFT JOIN node_conf_files USING (node_id)
-LEFT JOIN node_session USING (node_id);
+LEFT JOIN node_session USING (node_id)
+WHERE nodes.cached=False;
+
+CREATE VIEW view_foreign_nodes AS
+SELECT
+nodes.node_id,
+nodes.hostname,
+nodes.peer_id,
+nodes.boot_state,
+nodes.model,
+nodes.version,
+CAST(date_part('epoch', nodes.date_created) AS bigint) AS date_created,
+CAST(date_part('epoch', nodes.last_updated) AS bigint) AS last_updated,
+node_slices.slice_ids,
+nodes.deleted
+FROM nodes
+LEFT JOIN node_slices USING (node_id)
+WHERE nodes.cached=True AND nodes.deleted=False;
 
 CREATE VIEW view_nodegroups AS
 SELECT
@@ -937,35 +983,20 @@ VALUES
 ('pl', 'PlanetLab Central', 'PLC', 100);
 
 -- federation stuff starting here
-INSERT INTO roles (role_id, name) VALUES (3000, 'peer');
 
-CREATE TABLE peers (
-     peer_id  serial PRIMARY KEY, -- identifier
-     peername text NOT NULL,      -- free text
-     peer_url text NOT NULL,      -- the url of that peer's API
-     person_id integer REFERENCES persons NOT NULL, -- the account we use for logging in
-       
-     deleted boolean NOT NULL DEFAULT false
-) WITH OIDS;
+--CREATE TABLE foreign_nodes (
+--     foreign_node_id serial PRIMARY KEY, -- identifier
+--     hostname text NOT NULL, 
+--     boot_state text NOT NULL,
+--     peer_id integer REFERENCES peers NOT NULL,
+--       
+--     deleted boolean NOT NULL DEFAULT false
+--) WITH OIDS;
 
-CREATE TABLE foreign_nodes (
-     foreign_node_id serial PRIMARY KEY, -- identifier
-     hostname text NOT NULL, 
-     boot_state text NOT NULL,
-     peer_id integer REFERENCES peers NOT NULL,
-       
-     deleted boolean NOT NULL DEFAULT false
-) WITH OIDS;
+--CREATE VIEW peer_foreign_nodes AS
+--SELECT peer_id,
+--array_to_string(array_accum(foreign_node_id), ',') AS foreign_node_ids
+--FROM foreign_nodes
+--GROUP BY peer_id;
 
-CREATE VIEW peer_foreign_nodes AS
-SELECT peer_id,
-array_to_string(array_accum(foreign_node_id), ',') AS foreign_node_ids
-FROM foreign_nodes
-GROUP BY peer_id;
 
-CREATE VIEW view_peers AS
-SELECT 
-peers.*, 
-peer_foreign_nodes.foreign_node_ids 
-FROM peers
-LEFT JOIN peer_foreign_nodes USING (peer_id);