Minimal cleanup to import managers without Errors. NT.
[sfa.git] / sfa / senslab / slabdriver.py
1 from sfa.util.faults import MissingSfaInfo
2 from sfa.util.sfalogging import logger
3 from sfa.util.table import SfaTable
4 from sfa.util.defaultdict import defaultdict
5
6 from sfa.util.xrn import hrn_to_urn
7 from sfa.util.plxrn import slicename_to_hrn, hostname_to_hrn, hrn_to_pl_slicename, hrn_to_pl_login_base
8
9 ## thierry: everything that is API-related (i.e. handling incoming requests) 
10 # is taken care of 
11 # SlabDriver should be really only about talking to the senslab testbed
12
13 ## thierry : please avoid wildcard imports :)
14 from sfa.senslab.OARrestapi import *
15 from sfa.senslab.SenslabImportUsers import *
16
17 def list_to_dict(recs, key):
18     """
19     convert a list of dictionaries into a dictionary keyed on the 
20     specified dictionary key 
21     """
22    # print>>sys.stderr, " \r\n \t\t 1list_to_dict : rec %s  \r\n \t\t list_to_dict key %s" %(recs,key)   
23     keys = [rec[key] for rec in recs]
24     #print>>sys.stderr, " \r\n \t\t list_to_dict : rec %s  \r\n \t\t list_to_dict keys %s" %(recs,keys)   
25     return dict(zip(keys, recs))
26
27 # thierry : note
28 # this inheritance scheme is so that the driver object can receive
29 # GetNodes or GetSites sorts of calls directly
30 # and thus minimize the differences in the managers with the pl version
31 class SlabDriver (OARapi, SenslabImportUsers):
32
33     def __init__(self, config):
34         self.config=config
35         self.hrn = config.SFA_INTERFACE_HRN
36  
37         print >>sys.stderr, "\r\n_____________ SFA SENSLAB DRIVER \r\n" 
38         # thierry - just to not break the rest of this code
39         #self.oar = OARapi()
40         #self.users = SenslabImportUsers()
41         self.oar = self
42         self.users = self
43         self.time_format = "%Y-%m-%d %H:%M:%S"
44         #self.logger=sfa_logger()
45         #print >>sys.stderr, "\r\n \t\t___________PSFA SENSLAN /API.PY  __init__ STOP ",self.interface #dir(self)
46         
47     ##
48     # Convert SFA fields to PLC fields for use when registering up updating
49     # registry record in the PLC database
50     #
51     # @param type type of record (user, slice, ...)
52     # @param hrn human readable name
53     # @param sfa_fields dictionary of SFA fields
54     # @param pl_fields dictionary of PLC fields (output)
55
56     def sfa_fields_to_pl_fields(self, type, hrn, record):
57
58         def convert_ints(tmpdict, int_fields):
59             for field in int_fields:
60                 if field in tmpdict:
61                     tmpdict[field] = int(tmpdict[field])
62
63         pl_record = {}
64         #for field in record:
65         #    pl_record[field] = record[field]
66  
67         if type == "slice":
68             if not "instantiation" in pl_record:
69                 pl_record["instantiation"] = "plc-instantiated"
70             pl_record["name"] = hrn_to_pl_slicename(hrn)
71             if "url" in record:
72                pl_record["url"] = record["url"]
73             if "description" in record:
74                 pl_record["description"] = record["description"]
75             if "expires" in record:
76                 pl_record["expires"] = int(record["expires"])
77
78         elif type == "node":
79             if not "hostname" in pl_record:
80                 if not "hostname" in record:
81                     raise MissingSfaInfo("hostname")
82                 pl_record["hostname"] = record["hostname"]
83             if not "model" in pl_record:
84                 pl_record["model"] = "geni"
85
86         elif type == "authority":
87             pl_record["login_base"] = hrn_to_pl_login_base(hrn)
88
89             if not "name" in pl_record:
90                 pl_record["name"] = hrn
91
92             if not "abbreviated_name" in pl_record:
93                 pl_record["abbreviated_name"] = hrn
94
95             if not "enabled" in pl_record:
96                 pl_record["enabled"] = True
97
98             if not "is_public" in pl_record:
99                 pl_record["is_public"] = True
100
101         return pl_record
102
103     def fill_record_pl_info(self, records):
104         """
105         Fill in the planetlab specific fields of a SFA record. This
106         involves calling the appropriate PLC method to retrieve the 
107         database record for the object.
108         
109         PLC data is filled into the pl_info field of the record.
110     
111         @param record: record to fill in field (in/out param)     
112         """
113         # get ids by type
114         #print>>sys.stderr, "\r\n \r\rn \t\t >>>>>>>>>>fill_record_pl_info  records %s : "%(records)
115         node_ids, site_ids, slice_ids = [], [], [] 
116         person_ids, key_ids = [], []
117         type_map = {'node': node_ids, 'authority': site_ids,
118                     'slice': slice_ids, 'user': person_ids}
119                   
120         for record in records:
121             for type in type_map:
122                 #print>>sys.stderr, "\r\n \t\t \t fill_record_pl_info : type %s. record['pointer'] %s "%(type,record['pointer'])   
123                 if type == record['type']:
124                     type_map[type].append(record['pointer'])
125         #print>>sys.stderr, "\r\n \t\t \t fill_record_pl_info : records %s... \r\n \t\t \t fill_record_pl_info : type_map   %s"%(records,type_map)
126         # get pl records
127         nodes, sites, slices, persons, keys = {}, {}, {}, {}, {}
128         if node_ids:
129             node_list = self.oar.GetNodes( node_ids)
130             #print>>sys.stderr, " \r\n \t\t\t BEFORE LIST_TO_DICT_NODES node_ids : %s" %(node_ids)
131             nodes = list_to_dict(node_list, 'node_id')
132         if site_ids:
133             site_list = self.oar.GetSites( site_ids)
134             sites = list_to_dict(site_list, 'site_id')
135             #print>>sys.stderr, " \r\n \t\t\t  site_ids %s sites  : %s" %(site_ids,sites)           
136         if slice_ids:
137             slice_list = self.users.GetSlices( slice_ids)
138             slices = list_to_dict(slice_list, 'slice_id')
139         if person_ids:
140             #print>>sys.stderr, " \r\n \t\t \t fill_record_pl_info BEFORE GetPersons  person_ids: %s" %(person_ids)
141             person_list = self.users.GetPersons( person_ids)
142             persons = list_to_dict(person_list, 'person_id')
143             #print>>sys.stderr, "\r\n  fill_record_pl_info persons %s \r\n \t\t person_ids %s " %(persons, person_ids) 
144             for person in persons:
145                 key_ids.extend(persons[person]['key_ids'])
146                 #print>>sys.stderr, "\r\n key_ids %s " %(key_ids)
147
148         pl_records = {'node': nodes, 'authority': sites,
149                       'slice': slices, 'user': persons}
150
151         if key_ids:
152             key_list = self.users.GetKeys( key_ids)
153             keys = list_to_dict(key_list, 'key_id')
154            # print>>sys.stderr, "\r\n  fill_record_pl_info persons %s \r\n \t\t keys %s " %(keys) 
155         # fill record info
156         for record in records:
157             # records with pointer==-1 do not have plc info.
158             # for example, the top level authority records which are
159             # authorities, but not PL "sites"
160             if record['pointer'] == -1:
161                 continue
162            
163             for type in pl_records:
164                 if record['type'] == type:
165                     if record['pointer'] in pl_records[type]:
166                         record.update(pl_records[type][record['pointer']])
167                         break
168             # fill in key info 
169             if record['type'] == 'user':
170                  if 'key_ids' not in record:
171                         #print>>sys.stderr, " NO_KEY_IDS fill_record_pl_info key_ids record: %s" %(record)
172                         logger.info("user record has no 'key_ids' - need to import  ?")
173                  else:
174                         pubkeys = [keys[key_id]['key'] for key_id in record['key_ids'] if key_id in keys] 
175                         record['keys'] = pubkeys
176                         
177         #print>>sys.stderr, "\r\n \r\rn \t\t <<<<<<<<<<<<<<<<<< fill_record_pl_info  records %s : "%(records)
178         # fill in record hrns
179         records = self.fill_record_hrns(records)   
180
181         return records
182
183     def fill_record_hrns(self, records):
184         """
185         convert pl ids to hrns
186         """
187         #print>>sys.stderr, "\r\n \r\rn \t\t \t >>>>>>>>>>>>>>>>>>>>>> fill_record_hrns records %s : "%(records)  
188         # get ids
189         slice_ids, person_ids, site_ids, node_ids = [], [], [], []
190         for record in records:
191             #print>>sys.stderr, "\r\n \r\rn \t\t \t record %s : "%(record)
192             if 'site_id' in record:
193                 site_ids.append(record['site_id'])
194             if 'site_ids' in records:
195                 site_ids.extend(record['site_ids'])
196             if 'person_ids' in record:
197                 person_ids.extend(record['person_ids'])
198             if 'slice_ids' in record:
199                 slice_ids.extend(record['slice_ids'])
200             if 'node_ids' in record:
201                 node_ids.extend(record['node_ids'])
202
203         # get pl records
204         slices, persons, sites, nodes = {}, {}, {}, {}
205         if site_ids:
206             site_list = self.oar.GetSites( site_ids, ['site_id', 'login_base'])
207             sites = list_to_dict(site_list, 'site_id')
208             #print>>sys.stderr, " \r\n \r\n \t\t ____ site_list %s \r\n \t\t____ sites %s " % (site_list,sites)
209         if person_ids:
210             person_list = self.users.GetPersons( person_ids, ['person_id', 'email'])
211             #print>>sys.stderr, " \r\n \r\n   \t\t____ person_lists %s " %(person_list) 
212             persons = list_to_dict(person_list, 'person_id')
213         if slice_ids:
214             slice_list = self.users.GetSlices( slice_ids, ['slice_id', 'name'])
215             slices = list_to_dict(slice_list, 'slice_id')       
216         if node_ids:
217             node_list = self.oar.GetNodes( node_ids, ['node_id', 'hostname'])
218             nodes = list_to_dict(node_list, 'node_id')
219        
220         # convert ids to hrns
221         for record in records:
222              
223             # get all relevant data
224             type = record['type']
225             pointer = record['pointer']
226             auth_hrn = self.hrn
227             login_base = ''
228             if pointer == -1:
229                 continue
230
231             #print>>sys.stderr, " \r\n \r\n \t\t fill_record_hrns : sites %s \r\n \t\t record %s " %(sites, record)
232             if 'site_id' in record:
233                 site = sites[record['site_id']]
234                 #print>>sys.stderr, " \r\n \r\n \t\t \t fill_record_hrns : sites %s \r\n \t\t\t site sites[record['site_id']] %s " %(sites,site)        
235                 login_base = site['login_base']
236                 record['site'] = ".".join([auth_hrn, login_base])
237             if 'person_ids' in record:
238                 emails = [persons[person_id]['email'] for person_id in record['person_ids'] \
239                           if person_id in  persons]
240                 usernames = [email.split('@')[0] for email in emails]
241                 person_hrns = [".".join([auth_hrn, login_base, username]) for username in usernames]
242                 #print>>sys.stderr, " \r\n \r\n \t\t ____ person_hrns : %s " %(person_hrns)
243                 record['persons'] = person_hrns 
244             if 'slice_ids' in record:
245                 slicenames = [slices[slice_id]['name'] for slice_id in record['slice_ids'] \
246                               if slice_id in slices]
247                 slice_hrns = [slicename_to_hrn(auth_hrn, slicename) for slicename in slicenames]
248                 record['slices'] = slice_hrns
249             if 'node_ids' in record:
250                 hostnames = [nodes[node_id]['hostname'] for node_id in record['node_ids'] \
251                              if node_id in nodes]
252                 node_hrns = [hostname_to_hrn(auth_hrn, login_base, hostname) for hostname in hostnames]
253                 record['nodes'] = node_hrns
254             if 'site_ids' in record:
255                 login_bases = [sites[site_id]['login_base'] for site_id in record['site_ids'] \
256                                if site_id in sites]
257                 site_hrns = [".".join([auth_hrn, lbase]) for lbase in login_bases]
258                 record['sites'] = site_hrns
259         #print>>sys.stderr, "\r\n \r\rn \t\t \t <<<<<<<<<<<<<<<<<<<<<<<<  fill_record_hrns records %s : "%(records)  
260         return records   
261
262     def fill_record_sfa_info(self, records):
263
264         def startswith(prefix, values):
265             return [value for value in values if value.startswith(prefix)]
266         
267         SenslabUsers = SenslabImportUsers()
268         # get person ids
269         person_ids = []
270         site_ids = []
271         for record in records:
272             person_ids.extend(record.get("person_ids", []))
273             site_ids.extend(record.get("site_ids", [])) 
274             if 'site_id' in record:
275                 site_ids.append(record['site_id']) 
276                 
277         #print>>sys.stderr, "\r\n \r\n _fill_record_sfa_info ___person_ids %s \r\n \t\t site_ids %s " %(person_ids, site_ids)
278         
279         # get all pis from the sites we've encountered
280         # and store them in a dictionary keyed on site_id 
281         site_pis = {}
282         if site_ids:
283             pi_filter = {'|roles': ['pi'], '|site_ids': site_ids} 
284             pi_list = SenslabUsers.GetPersons( pi_filter, ['person_id', 'site_ids'])
285             #print>>sys.stderr, "\r\n \r\n _fill_record_sfa_info ___ GetPersons ['person_id', 'site_ids'] pi_ilist %s" %(pi_list)
286
287             for pi in pi_list:
288                 # we will need the pi's hrns also
289                 person_ids.append(pi['person_id'])
290                 
291                 # we also need to keep track of the sites these pis
292                 # belong to
293                 for site_id in pi['site_ids']:
294                     if site_id in site_pis:
295                         site_pis[site_id].append(pi)
296                     else:
297                         site_pis[site_id] = [pi]
298                  
299         # get sfa records for all records associated with these records.   
300         # we'll replace pl ids (person_ids) with hrns from the sfa records
301         # we obtain
302         
303         # get the sfa records
304         table = SfaTable()
305         person_list, persons = [], {}
306         person_list = table.find({'type': 'user', 'pointer': person_ids})
307         # create a hrns keyed on the sfa record's pointer.
308         # Its possible for  multiple records to have the same pointer so
309         # the dict's value will be a list of hrns.
310         persons = defaultdict(list)
311         for person in person_list:
312             persons[person['pointer']].append(person)
313
314         # get the pl records
315         pl_person_list, pl_persons = [], {}
316         pl_person_list = SenslabUsers.GetPersons(person_ids, ['person_id', 'roles'])
317         pl_persons = list_to_dict(pl_person_list, 'person_id')
318         #print>>sys.stderr, "\r\n \r\n _fill_record_sfa_info ___  _list %s \r\n \t\t SenslabUsers.GetPersons ['person_id', 'roles'] pl_persons %s \r\n records %s" %(pl_person_list, pl_persons,records) 
319         # fill sfa info
320         
321         for record in records:
322             # skip records with no pl info (top level authorities)
323             #Sandrine 24 oct 11 2 lines
324             #if record['pointer'] == -1:
325                 #continue 
326             sfa_info = {}
327             type = record['type']
328             if (type == "slice"):
329                 # all slice users are researchers
330                 #record['geni_urn'] = hrn_to_urn(record['hrn'], 'slice')  ? besoin ou pas ?
331                 record['PI'] = []
332                 record['researcher'] = []
333                 for person_id in record.get('person_ids', []):
334                          #Sandrine 24 oct 11 line
335                 #for person_id in record['person_ids']:
336                     hrns = [person['hrn'] for person in persons[person_id]]
337                     record['researcher'].extend(hrns)                
338
339                 # pis at the slice's site
340                 pl_pis = site_pis[record['site_id']]
341                 pi_ids = [pi['person_id'] for pi in pl_pis]
342                 for person_id in pi_ids:
343                     hrns = [person['hrn'] for person in persons[person_id]]
344                     record['PI'].extend(hrns)
345                 record['geni_urn'] = hrn_to_urn(record['hrn'], 'slice')
346                 record['geni_creator'] = record['PI'] 
347                 
348             elif (type == "authority"):
349                 record['PI'] = []
350                 record['operator'] = []
351                 record['owner'] = []
352                 for pointer in record['person_ids']:
353                     if pointer not in persons or pointer not in pl_persons:
354                         # this means there is not sfa or pl record for this user
355                         continue   
356                     hrns = [person['hrn'] for person in persons[pointer]] 
357                     roles = pl_persons[pointer]['roles']   
358                     if 'pi' in roles:
359                         record['PI'].extend(hrns)
360                     if 'tech' in roles:
361                         record['operator'].extend(hrns)
362                     if 'admin' in roles:
363                         record['owner'].extend(hrns)
364                     # xxx TODO: OrganizationName
365             elif (type == "node"):
366                 sfa_info['dns'] = record.get("hostname", "")
367                 # xxx TODO: URI, LatLong, IP, DNS
368     
369             elif (type == "user"):
370                  sfa_info['email'] = record.get("email", "")
371                  sfa_info['geni_urn'] = hrn_to_urn(record['hrn'], 'user')
372                  sfa_info['geni_certificate'] = record['gid'] 
373                 # xxx TODO: PostalAddress, Phone
374                 
375             #print>>sys.stderr, "\r\n \r\rn \t\t \t <<<<<<<<<<<<<<<<<<<<<<<<  fill_record_sfa_info sfa_info %s  \r\n record %s : "%(sfa_info,record)  
376             record.update(sfa_info)
377
378     def fill_record_info(self, records):
379         """
380         Given a SFA record, fill in the PLC specific and SFA specific
381         fields in the record. 
382         """
383         #print >>sys.stderr, "\r\n \t\t fill_record_info %s"%(records)
384         if not isinstance(records, list):
385             records = [records]
386         #print >>sys.stderr, "\r\n \t\t BEFORE fill_record_pl_info %s" %(records)       
387         self.fill_record_pl_info(records)
388         #print >>sys.stderr, "\r\n \t\t after fill_record_pl_info %s" %(records)        
389         self.fill_record_sfa_info(records)
390         #print >>sys.stderr, "\r\n \t\t after fill_record_sfa_info"
391         
392     def update_membership_list(self, oldRecord, record, listName, addFunc, delFunc):
393         # get a list of the HRNs tht are members of the old and new records
394         if oldRecord:
395             oldList = oldRecord.get(listName, [])
396         else:
397             oldList = []     
398         newList = record.get(listName, [])
399
400         # if the lists are the same, then we don't have to update anything
401         if (oldList == newList):
402             return
403
404         # build a list of the new person ids, by looking up each person to get
405         # their pointer
406         newIdList = []
407         table = SfaTable()
408         records = table.find({'type': 'user', 'hrn': newList})
409         for rec in records:
410             newIdList.append(rec['pointer'])
411
412         # build a list of the old person ids from the person_ids field 
413         if oldRecord:
414             oldIdList = oldRecord.get("person_ids", [])
415             containerId = oldRecord.get_pointer()
416         else:
417             # if oldRecord==None, then we are doing a Register, instead of an
418             # update.
419             oldIdList = []
420             containerId = record.get_pointer()
421
422     # add people who are in the new list, but not the oldList
423         for personId in newIdList:
424             if not (personId in oldIdList):
425                 addFunc(self.plauth, personId, containerId)
426
427         # remove people who are in the old list, but not the new list
428         for personId in oldIdList:
429             if not (personId in newIdList):
430                 delFunc(self.plauth, personId, containerId)
431
432     def update_membership(self, oldRecord, record):
433         if record.type == "slice":
434             self.update_membership_list(oldRecord, record, 'researcher',
435                                         self.users.AddPersonToSlice,
436                                         self.users.DeletePersonFromSlice)
437         elif record.type == "authority":
438             # xxx TODO
439             pass
440
441 ### thierry
442 # I don't think you plan on running a component manager at this point
443 # let me clean up the mess of ComponentAPI that is deprecated anyways