From df7b2e1295ad9bfb249e12313b5a618546612720 Mon Sep 17 00:00:00 2001 From: Thierry Parmentelat Date: Wed, 8 Nov 2006 17:34:07 +0000 Subject: [PATCH] federation in progress - associate a local slice to a foreign node --- PLC/ForeignNodes.py | 27 ++++++--- PLC/Methods/AddSliceToNodes.py | 21 +++++-- PLC/Methods/GetForeignNodes.py | 2 +- PLC/Methods/RefreshPeer.py | 103 +++++++++++++++++++++++++++++++++ PLC/Methods/UpdatePeer.py | 88 ---------------------------- PLC/Nodes.py | 7 ++- PLC/Peers.py | 6 +- PLC/Slices.py | 8 ++- planetlab4.sql | 97 ++++++++++++++++++++----------- 9 files changed, 217 insertions(+), 142 deletions(-) create mode 100644 PLC/Methods/RefreshPeer.py delete mode 100644 PLC/Methods/UpdatePeer.py diff --git a/PLC/ForeignNodes.py b/PLC/ForeignNodes.py index 0a135ea..9bc0c63 100644 --- a/PLC/ForeignNodes.py +++ b/PLC/ForeignNodes.py @@ -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)) diff --git a/PLC/Methods/AddSliceToNodes.py b/PLC/Methods/AddSliceToNodes.py index 3b194b4..37ad4a3 100644 --- a/PLC/Methods/AddSliceToNodes.py +++ b/PLC/Methods/AddSliceToNodes.py @@ -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] diff --git a/PLC/Methods/GetForeignNodes.py b/PLC/Methods/GetForeignNodes.py index 230240b..e9c2321 100644 --- a/PLC/Methods/GetForeignNodes.py +++ b/PLC/Methods/GetForeignNodes.py @@ -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 index 0000000..099b737 --- /dev/null +++ b/PLC/Methods/RefreshPeer.py @@ -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 index 1249a47..0000000 --- a/PLC/Methods/UpdatePeer.py +++ /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 ] - diff --git a/PLC/Nodes.py b/PLC/Nodes.py index 16c63cb..a591601 100644 --- a/PLC/Nodes.py +++ b/PLC/Nodes.py @@ -4,7 +4,7 @@ # Mark Huang # 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 diff --git a/PLC/Peers.py b/PLC/Peers.py index 9686044..c887702 100644 --- a/PLC/Peers.py +++ b/PLC/Peers.py @@ -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: diff --git a/PLC/Slices.py b/PLC/Slices.py index 8eec308..81df6c9 100644 --- a/PLC/Slices.py +++ b/PLC/Slices.py @@ -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'] diff --git a/planetlab4.sql b/planetlab4.sql index 5d3e264..9e1d1d5 100644 --- a/planetlab4.sql +++ b/planetlab4.sql @@ -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); -- 2.43.0