1 from PLC.Faults import *
2 from PLC.Parameter import Parameter
3 from PLC.Filter import Filter
4 from PLC.Table import Row, Table
12 def class_attributes (classname):
13 """ locates various attributes defined in the row class """
14 topmodule = __import__ ('PLC.%ss'%classname)
15 module = topmodule.__dict__['%ss'%classname]
16 # local row-like class, e.g. Node
17 row_class = module.__dict__['%s'%classname]
18 # local tab-like class, e.g. Nodes
19 table_class = module.__dict__['%ss'%classname]
21 return {'row_class':row_class,
22 'table_class':table_class,
23 'primary_key': row_class.__dict__['primary_key'],
24 'class_key': row_class.__dict__['class_key'],
25 'foreign_fields': row_class.__dict__['foreign_fields'],
26 'foreign_xrefs': row_class.__dict__['foreign_xrefs'],
31 # an attempt to provide genericity in the caching algorithm
33 # the Peer object we are syncing with
34 def __init__ (self, api, peer, peer_server, auth):
39 assert isinstance(peer,PLC.Peers.Peer)
41 self.peer_server = peer_server
46 def __init__ (self, api, classname, alien_objects):
48 attrs = class_attributes (classname)
49 self.primary_key = attrs['primary_key']
50 self.class_key = attrs['class_key']
52 # cannot use dict, it's acquired by xmlrpc and is untyped
53 self.alien_objects_byid = dict( [ (x[self.primary_key],x) for x in alien_objects ] )
55 # retrieve local objects
56 local_objects = attrs['table_class'] (api)
57 self.local_objects_byname = local_objects.dict(self.class_key)
59 verbose ('Transcoder init :',classname,
60 self.alien_objects_byid.keys(),
61 self.local_objects_byname.keys())
63 def transcode (self, alien_id):
64 """ transforms an alien id into a local one """
65 # locate alien obj from alien_id
66 verbose ('entering transcode with alien_id',alien_id,)
67 alien_object=self.alien_objects_byid[alien_id]
68 verbose ('located alien_obj',)
69 name = alien_object [self.class_key]
70 verbose ('got name',name,)
71 local_object=self.local_objects_byname[name]
72 verbose ('found local obj')
73 local_id=local_object[self.primary_key]
74 verbose ('and local_id',local_id)
80 def __init__ (self, api, tablename, class1, class2):
82 self.tablename = tablename
83 self.lowerclass1 = class1.lower()
84 self.lowerclass2 = class2.lower()
86 def delete_old_items (self, id1, id2_set):
89 sql += "DELETE FROM %s WHERE %s_id=%d"%(self.tablename,self.lowerclass1,id1)
90 sql += " AND %s_id IN ("%self.lowerclass2
91 sql += ",".join([str(i) for i in id2_set])
95 def insert_new_items (self, id1, id2_set):
96 ### xxx needs to be optimized
97 ### tried to figure a way to use a single sql statement
98 ### like: insert into table (x,y) values (1,2),(3,4);
99 ### but apparently this is not supported under postgresql
101 sql = "INSERT INTO %s VALUES (%d,%d)"%(self.tablename,id1,id2)
104 def update_item (self, id1, old_id2s, new_id2s):
105 news = set (new_id2s)
106 olds = set (old_id2s)
107 to_delete = olds-news
108 self.delete_old_items (id1, to_delete)
109 to_create = news-olds
110 self.insert_new_items (id1, to_create)
113 # classname: the type of objects we are talking about; e.g. 'Slice'
114 # peer_object_list list of objects at a given peer - e.g. peer.GetSlices()
115 # alien_xref_objs_dict : a dict {'classname':alien_obj_list} e.g. {'Node':peer.GetNodes()}
116 # his must match the keys in xref_specs
117 # lambda_ignore : the alien objects are ignored if this returns true
118 def update_table (self,
121 alien_xref_objs_dict = {},
122 lambda_ignore=lambda x:False):
125 peer_id = peer['peer_id']
127 attrs = class_attributes (classname)
128 row_class = attrs['row_class']
129 table_class = attrs['table_class']
130 primary_key = attrs['primary_key']
131 class_key = attrs['class_key']
132 foreign_fields = attrs['foreign_fields']
133 foreign_xrefs = attrs['foreign_xrefs']
135 ## allocate transcoders and xreftables once, for each item in foreign_xrefs
136 # create a dict 'classname' -> {'transcoder' : ..., 'xref_table' : ...}
139 {'transcoder':Cache.Transcoder (self.api,xref_classname,alien_xref_objs_dict[xref_classname]),
140 'xref_table':Cache.XrefTable (self.api,xref_spec['table'],classname,xref_classname)})
141 for xref_classname,xref_spec in foreign_xrefs.iteritems()])
143 ### get current local table
144 # get ALL local objects so as to cope with
145 # (*) potential moves between plcs
146 # (*) or naming conflicts
147 local_objects = table_class (self.api)
148 ### index upon class_key for future searches
149 #verbose ('local objects:',local_objects)
150 verbose ('class_key',class_key)
151 local_objects_index = local_objects.dict(class_key)
152 verbose ('update_table',classname,local_objects_index.keys())
154 ### mark entries for this peer outofdate
156 for local_object in local_objects:
157 if local_object['peer_id'] == peer_id:
158 local_object.uptodate=False
161 local_object.uptodate=True
164 # scan the peer's local objects
165 for alien_object in alien_object_list:
167 object_name = alien_object[class_key]
169 ### ignore, e.g. system-wide slices
170 if lambda_ignore(alien_object):
171 verbose('Ignoring',object_name)
174 verbose ('update_table - Considering',object_name)
178 ### We know about this object already
179 local_object = local_objects_index[object_name]
180 if local_object ['peer_id'] is None:
182 print '==================== We are in trouble here'
183 print 'The %s object named %s is natively defined twice'%(classname,object_name)
184 print 'Once on this PLC and once on peer %d'%peer_id
185 print 'We dont raise an exception so that the remaining updates can still take place'
187 if local_object['peer_id'] != peer_id:
188 ### the object has changed its plc,
189 ### Note, this is not problematic here because both definitions are remote
190 ### we can assume the object just moved
191 ### needs to update peer_id though
192 local_object['peer_id'] = peer_id
193 verbose ('update_table FOUND',object_name)
195 ### create a new entry
196 local_object = row_class(self.api,
197 {class_key :object_name,'peer_id':peer_id})
199 local_objects_index[class_key]=local_object
200 verbose ('update_table CREATED',object_name)
203 for field in foreign_fields:
204 local_object[field]=alien_object[field]
206 # this row is now valid
207 local_object.uptodate=True
212 for xref_classname,xref_spec in foreign_xrefs.iteritems():
213 field=xref_spec['field']
214 alien_xref_obj_list = alien_xref_objs_dict[xref_classname]
215 alien_value = alien_object[field]
216 transcoder = accessories[xref_classname]['transcoder']
217 if isinstance (alien_value,list):
218 verbose ('update_table list-transcoding ',xref_classname,' aliens=',alien_value,)
220 for a in alien_value:
222 local_values.append(transcoder.transcode(a))
224 # could not transcode - might be from another peer that we dont know about..
226 verbose (" transcoded as ",local_values)
227 xref_table = accessories[xref_classname]['xref_table']
228 # newly created objects dont have xrefs yet
230 former_xrefs=local_object[xref_spec['field']]
233 xref_table.update_item (local_object[primary_key],
236 elif isinstance (alien_value,int):
237 verbose ('update_table atom-transcoding ',xref_classname,' aliens=',alien_value,)
238 new_value = transcoder.transcode(alien_value)
239 local_object[field] = new_value
242 ### delete entries that are not uptodate
243 for local_object in local_objects:
244 if not local_object.uptodate:
245 local_object.delete()
249 ### return delta in number of objects
250 return new_count-old_count
252 def get_locals (self, list):
253 return [x for x in list if x['peer_id'] is None]
255 def refresh_peer (self):
257 # so as to minimize the numer of requests
258 # we get all objects in a single call and sort afterwards
259 # xxx ideally get objects either local or the ones attached here
260 # requires to know remote peer's peer_id for ourselves, mmhh..
261 # does not make any difference in a 2-peer deployment though
264 all_sites = self.peer_server.GetSites(self.auth)
265 local_sites = self.get_locals (all_sites)
266 nb_new_sites = self.update_table('Site', local_sites)
269 all_keys = self.peer_server.GetKeys(self.auth)
270 local_keys = self.get_locals (all_keys)
271 nb_new_keys = self.update_table('Key', local_keys)
274 all_nodes = self.peer_server.GetNodes(self.auth)
275 local_nodes = self.get_locals(all_nodes)
276 nb_new_nodes = self.update_table('Node', local_nodes,
277 { 'Site' : all_sites } )
280 all_persons = self.peer_server.GetPersons(self.auth)
281 local_persons = self.get_locals(all_persons)
282 nb_new_persons = self.update_table ('Person', local_persons,
283 { 'Key': all_keys, 'Site' : all_sites } )
286 local_slices = self.peer_server.GetSlices(self.auth,{'peer_id':None})
288 def is_system_slice (slice):
289 return slice['creator_person_id'] == 1
291 nb_new_slices = self.update_table ('Slice', local_slices,
292 {'Node': all_nodes, 'Person': all_persons},
295 return {'plcname':self.api.config.PLC_NAME,
296 'new_sites':nb_new_sites,
297 'new_keys':nb_new_keys,
298 'new_nodes':nb_new_nodes,
299 'new_persons':nb_new_persons,
300 'new_slices':nb_new_slices}