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_id, peer_server, auth):
39 # assert isinstance(peer,PLC.Peers.Peer)
41 self.peer_id = peer_id
42 self.peer_server = peer_server
47 def __init__ (self, api, classname, alien_objects):
49 attrs = class_attributes (classname)
50 self.primary_key = attrs['primary_key']
51 self.class_key = attrs['class_key']
53 # cannot use dict, it's acquired by xmlrpc and is untyped
54 self.alien_objects_byid = dict( [ (x[self.primary_key],x) for x in alien_objects ] )
56 # retrieve local objects
57 local_objects = attrs['table_class'] (api)
58 self.local_objects_byname = local_objects.dict(self.class_key)
60 verbose ('Transcoder init :',classname,
61 self.alien_objects_byid.keys(),
62 self.local_objects_byname.keys())
64 def transcode (self, alien_id):
65 """ transforms an alien id into a local one """
66 # locate alien obj from alien_id
67 verbose ('entering transcode with alien_id',alien_id,)
68 alien_object=self.alien_objects_byid[alien_id]
69 verbose ('located alien_obj',)
70 name = alien_object [self.class_key]
71 verbose ('got name',name,)
72 local_object=self.local_objects_byname[name]
73 verbose ('found local obj')
74 local_id=local_object[self.primary_key]
75 verbose ('and local_id',local_id)
81 def __init__ (self, api, tablename, class1, class2):
83 self.tablename = tablename
84 self.lowerclass1 = class1.lower()
85 self.lowerclass2 = class2.lower()
87 def delete_old_items (self, id1, id2_set):
90 sql += "DELETE FROM %s WHERE %s_id=%d"%(self.tablename,self.lowerclass1,id1)
91 sql += " AND %s_id IN ("%self.lowerclass2
92 sql += ",".join([str(i) for i in id2_set])
96 def insert_new_items (self, id1, id2_set):
97 ### xxx needs to be optimized
98 ### tried to figure a way to use a single sql statement
99 ### like: insert into table (x,y) values (1,2),(3,4);
100 ### but apparently this is not supported under postgresql
102 sql = "INSERT INTO %s VALUES (%d,%d)"%(self.tablename,id1,id2)
105 def update_item (self, id1, old_id2s, new_id2s):
106 news = set (new_id2s)
107 olds = set (old_id2s)
108 to_delete = olds-news
109 self.delete_old_items (id1, to_delete)
110 to_create = news-olds
111 self.insert_new_items (id1, to_create)
114 # classname: the type of objects we are talking about; e.g. 'Slice'
115 # peer_object_list list of objects at a given peer - e.g. peer.GetSlices()
116 # alien_xref_objs_dict : a dict {'classname':alien_obj_list} e.g. {'Node':peer.GetNodes()}
117 # his must match the keys in xref_specs
118 # lambda_ignore : the alien objects are ignored if this returns true
119 def update_table (self,
122 alien_xref_objs_dict = {},
123 lambda_ignore=lambda x:False,
124 report_name_conflicts = True):
127 # peer_id = peer['peer_id']
130 attrs = class_attributes (classname)
131 row_class = attrs['row_class']
132 table_class = attrs['table_class']
133 primary_key = attrs['primary_key']
134 class_key = attrs['class_key']
135 foreign_fields = attrs['foreign_fields']
136 foreign_xrefs = attrs['foreign_xrefs']
138 ## allocate transcoders and xreftables once, for each item in foreign_xrefs
139 # create a dict 'classname' -> {'transcoder' : ..., 'xref_table' : ...}
142 {'transcoder':Cache.Transcoder (self.api,xref_classname,alien_xref_objs_dict[xref_classname]),
143 'xref_table':Cache.XrefTable (self.api,xref_spec['table'],classname,xref_classname)})
144 for xref_classname,xref_spec in foreign_xrefs.iteritems()])
146 ### get current local table
147 # get ALL local objects so as to cope with
148 # (*) potential moves between plcs
149 # (*) or naming conflicts
150 local_objects = table_class (self.api)
151 ### index upon class_key for future searches
152 #verbose ('local objects:',local_objects)
153 verbose ('class_key',class_key)
154 local_objects_index = local_objects.dict(class_key)
155 verbose ('update_table',classname,local_objects_index.keys())
157 ### mark entries for this peer outofdate
159 for local_object in local_objects:
160 if local_object['peer_id'] == peer_id:
161 local_object.uptodate=False
164 local_object.uptodate=True
167 # scan the peer's local objects
168 for alien_object in alien_object_list:
170 object_name = alien_object[class_key]
172 ### ignore, e.g. system-wide slices
173 if lambda_ignore(alien_object):
174 verbose('Ignoring',object_name)
177 verbose ('update_table - Considering',object_name)
181 ### We know about this object already
182 local_object = local_objects_index[object_name]
183 if local_object ['peer_id'] is None:
184 if report_name_conflicts:
186 print '==================== We are in trouble here'
187 print 'The %s object named %s is natively defined twice'%(classname,object_name)
188 print 'Once on this PLC and once on peer %d'%peer_id
189 print 'We dont raise an exception so that the remaining updates can still take place'
191 if local_object['peer_id'] != peer_id:
192 ### the object has changed its plc,
193 ### Note, this is not problematic here because both definitions are remote
194 ### we can assume the object just moved
195 ### needs to update peer_id though
196 local_object['peer_id'] = peer_id
197 verbose ('update_table FOUND',object_name)
199 ### create a new entry
200 local_object = row_class(self.api,
201 {class_key :object_name,'peer_id':peer_id})
203 local_objects_index[class_key]=local_object
204 verbose ('update_table CREATED',object_name)
207 for field in foreign_fields:
208 local_object[field]=alien_object[field]
210 # this row is now valid
211 local_object.uptodate=True
216 for xref_classname,xref_spec in foreign_xrefs.iteritems():
217 field=xref_spec['field']
218 alien_xref_obj_list = alien_xref_objs_dict[xref_classname]
219 alien_value = alien_object[field]
220 transcoder = accessories[xref_classname]['transcoder']
221 if isinstance (alien_value,list):
222 verbose ('update_table list-transcoding ',xref_classname,' aliens=',alien_value,)
224 for a in alien_value:
226 local_values.append(transcoder.transcode(a))
228 # could not transcode - might be from another peer that we dont know about..
230 verbose (" transcoded as ",local_values)
231 xref_table = accessories[xref_classname]['xref_table']
232 # newly created objects dont have xrefs yet
234 former_xrefs=local_object[xref_spec['field']]
237 xref_table.update_item (local_object[primary_key],
240 elif isinstance (alien_value,int):
241 verbose ('update_table atom-transcoding ',xref_classname,' aliens=',alien_value,)
242 new_value = transcoder.transcode(alien_value)
243 local_object[field] = new_value
246 ### delete entries that are not uptodate
247 for local_object in local_objects:
248 if not local_object.uptodate:
249 local_object.delete()
253 ### return delta in number of objects
254 return new_count-old_count
256 def get_locals (self, list):
257 return [x for x in list if x['peer_id'] is None]
259 def refresh_peer (self):
261 # so as to minimize the numer of requests
262 # we get all objects in a single call and sort afterwards
263 # xxx ideally get objects either local or the ones attached here
264 # requires to know remote peer's peer_id for ourselves, mmhh..
265 # does not make any difference in a 2-peer deployment though
267 ### uses GetPeerData to gather all info in a single xmlrpc request
269 # xxx see also GetPeerData - peer_id arg unused yet
270 all_data = self.peer_server.GetPeerData (self.auth,0)
273 #all_sites = self.peer_server.GetSites(self.auth)
274 all_sites = all_data['Sites']
275 local_sites = self.get_locals (all_sites)
276 nb_new_sites = self.update_table('Site', local_sites)
279 #all_keys = self.peer_server.GetKeys(self.auth)
280 all_keys = all_data['Keys']
281 local_keys = self.get_locals (all_keys)
282 nb_new_keys = self.update_table('Key', local_keys)
285 #all_nodes = self.peer_server.GetNodes(self.auth)
286 all_nodes = all_data['Nodes']
287 local_nodes = self.get_locals(all_nodes)
288 nb_new_nodes = self.update_table('Node', local_nodes,
289 { 'Site' : all_sites } )
292 #all_persons = self.peer_server.GetPersons(self.auth)
293 all_persons = all_data['Persons']
294 local_persons = self.get_locals(all_persons)
295 nb_new_persons = self.update_table ('Person', local_persons,
296 { 'Key': all_keys, 'Site' : all_sites } )
298 # refresh slice attribute types
299 all_slice_attribute_types = all_data ['SliceAttibuteTypes']
300 local_slice_attribute_types = self.get_locals(all_slice_attribute_types)
301 nb_new_slice_attribute_types = self.update_table ('SliceAttributeType',
302 local_slice_attribute_types,
303 report_name_conflicts = False)
306 #local_slices = self.peer_server.GetSlices(self.auth,{'peer_id':None})
307 local_slices = all_data['Slices']
309 def is_system_slice (slice):
310 return slice['creator_person_id'] == 1
312 nb_new_slices = self.update_table ('Slice', local_slices,
313 {'Node': all_nodes, 'Person': all_persons},
316 ### returned as-is by RefreshPeer
317 return {'plcname':self.api.config.PLC_NAME,
318 'new_sites':nb_new_sites,
319 'new_keys':nb_new_keys,
320 'new_nodes':nb_new_nodes,
321 'new_persons':nb_new_persons,
322 'new_slice_attribute_types':nb_new_slice_attribute_types,
323 'new_slices':nb_new_slices,