Merge branch 'master' of ssh://git.onelab.eu/git/sfa
[sfa.git] / sfa / storage / record.py
1 ##
2 # Implements support for SFA records
3 #
4 # TODO: Use existing PLC database methods? or keep this separate?
5 ##
6
7 from types import StringTypes
8 from sfa.trust.gid import GID
9 from sfa.storage.parameter import Parameter
10 from sfa.util.xrn import get_authority
11 from sfa.storage.row import Row
12 from sfa.util.xml import XML 
13 from sfa.util.sfalogging import logger
14
15 class SfaRecord(Row):
16     """ 
17     The SfaRecord class implements an SFA Record. A SfaRecord is a tuple
18     (Hrn, GID, Type, Info).
19  
20     Hrn specifies the Human Readable Name of the object
21     GID is the GID of the object
22     Type is user | authority | slice | component
23  
24     Info is comprised of the following sub-fields
25            pointer = a pointer to the record in the PL database
26  
27     The pointer is interpreted depending on the type of the record. For example,
28     if the type=="user", then pointer is assumed to be a person_id that indexes
29     into the persons table.
30  
31     A given HRN may have more than one record, provided that the records are
32     of different types.
33     """
34
35 #    table_name = 'sfa'
36 #    primary_key = 'record_id'
37
38     ### the wsdl generator assumes this is named 'fields'
39     internal_fields = {
40         'record_id': Parameter(int, "An id that uniquely identifies this record", ro=True),
41         'pointer': Parameter(int, "An id that uniquely identifies this record in an external database")
42     }
43
44     fields = {
45         'authority': Parameter(str, "The authority for this record"),
46         'peer_authority': Parameter(str, "The peer authority for this record"),
47         'hrn': Parameter(str, "Human readable name of object"),
48         'gid': Parameter(str, "GID of the object"),
49         'type': Parameter(str, "Record type"),
50         'last_updated': Parameter(int, "Date and time of last update", ro=True),
51         'date_created': Parameter(int, "Date and time this record was created", ro=True),
52     }
53     all_fields = dict(fields.items() + internal_fields.items())
54     ##
55     # Create an SFA Record
56     #
57     # @param name if !=None, assign the name of the record
58     # @param gid if !=None, assign the gid of the record
59     # @param type one of user | authority | slice | component
60     # @param pointer is a pointer to a PLC record
61     # @param dict if !=None, then fill in this record from the dictionary
62
63     def __init__(self, hrn=None, gid=None, type=None, pointer=None, authority=None, 
64                  peer_authority=None, dict=None, string=None):
65         self.dirty = True
66         self.hrn = None
67         self.gid = None
68         self.type = None
69         self.pointer = None
70         self.set_peer_auth(peer_authority)
71         self.set_authority(authority)
72         if hrn:
73             self.set_name(hrn)
74         if gid:
75             self.set_gid(gid)
76         if type:
77             self.set_type(type)
78         if pointer:
79             self.set_pointer(pointer)
80         if dict:
81             self.load_from_dict(dict)
82         if string:
83             self.load_from_string(string)
84
85
86     def validate_last_updated(self, last_updated):
87         return time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime())
88         
89     def update(self, new_dict):
90         if isinstance(new_dict, list):
91             new_dict = new_dict[0]
92
93         # Convert any boolean strings to real bools
94         for key in new_dict:
95             if isinstance(new_dict[key], StringTypes):
96                 if new_dict[key].lower() in ["true"]:
97                     new_dict[key] = True
98                 elif new_dict[key].lower() in ["false"]:
99                     new_dict[key] = False
100         dict.update(self, new_dict)
101
102     ##
103     # Set the name of the record
104     #
105     # @param hrn is a string containing the HRN
106
107     def set_name(self, hrn):
108         """
109         Set the name of the record
110         """
111         self.hrn = hrn
112         self['hrn'] = hrn
113         self.dirty = True
114
115     def set_authority(self, authority):
116         """
117         Set the authority
118         """
119         if not authority:
120             authority = ""
121         self.authority = authority
122         self['authority'] = authority
123         self.dirty = True    
124         
125
126     ##
127     # Set the GID of the record
128     #
129     # @param gid is a GID object or the string representation of a GID object
130
131     def set_gid(self, gid):
132         """
133         Set the GID of the record
134         """
135
136         if isinstance(gid, StringTypes):
137             self.gid = gid
138             self['gid'] = gid
139         else:
140             self.gid = gid.save_to_string(save_parents=True)
141             self['gid'] = gid.save_to_string(save_parents=True)
142         self.dirty = True
143
144     ##
145     # Set the type of the record
146     #
147     # @param type is a string: user | authority | slice | component
148
149     def set_type(self, type):
150         """
151         Set the type of the record
152         """
153         self.type = type
154         self['type'] = type
155         self.dirty = True
156
157     ##
158     # Set the pointer of the record
159     #
160     # @param pointer is an integer containing the ID of a PLC record
161
162     def set_pointer(self, pointer):
163         """
164         Set the pointer of the record
165         """
166         self.pointer = pointer
167         self['pointer'] = pointer
168         self.dirty = True
169
170
171     def set_peer_auth(self, peer_authority):
172         self.peer_authority = peer_authority
173         self['peer_authority'] = peer_authority
174         self.dirty = True
175
176     ##
177     # Return the name (HRN) of the record
178
179     def get_name(self):
180         """
181         Return the name (HRN) of the record
182         """
183         return self.hrn
184
185     ##
186     # Return the type of the record
187
188     def get_type(self):
189         """
190         Return the type of the record
191         """
192         return self.type
193
194     ##
195     # Return the pointer of the record. The pointer is an integer that may be
196     # used to look up the record in the PLC database. The evaluation of pointer
197     # depends on the type of the record
198
199     def get_pointer(self):
200         """
201         Return the pointer of the record. The pointer is an integer that may be
202         used to look up the record in the PLC database. The evaluation of pointer
203         depends on the type of the record
204         """
205         return self.pointer
206
207     ##
208     # Return the GID of the record, in the form of a GID object
209     # TODO: not the best name for the function, because we have things called
210     # gidObjects in the Cred
211
212     def get_gid_object(self):
213         """
214         Return the GID of the record, in the form of a GID object
215         """
216         return GID(string=self.gid)
217
218     ##
219     # Returns the value of a field
220
221     def get_field(self, fieldname, default=None):
222         # sometimes records act like classes, and sometimes they act like dicts
223         try:
224             return getattr(self, fieldname)
225         except AttributeError:
226             try:
227                  return self[fieldname]
228             except KeyError:
229                  if default != None:
230                      return default
231                  else:
232                      raise
233
234     ##
235     # Returns a list of field names in this record. 
236
237     def get_field_names(self):
238         """
239         Returns a list of field names in this record.
240         """
241         return self.fields.keys()
242
243     ##
244     # Given a field name ("hrn", "gid", ...) return the value of that field.
245     #
246     # @param fieldname is the name of field to be returned
247
248     def get_field_value_string(self, fieldname):
249         """
250         Given a field name ("hrn", "gid", ...) return the value of that field.
251         """
252         if fieldname == "authority":
253             val = get_authority(self['hrn'])
254         else:
255             try:
256                 val = getattr(self, fieldname)
257             except:
258                 val = self[fieldname] 
259         if isinstance(val, str):
260             return "'" + str(val) + "'"
261         else:
262             return str(val)
263
264     ##
265     # Given a list of field names, return a list of values for those public.
266     #
267     # @param fieldnames is a list of field names
268
269     def get_field_value_strings(self, fieldnames):
270         """
271         Given a list of field names, return a list of values for those public.
272         """
273         return [ self.get_field_value_string (fieldname) for fieldname in fieldnames ]
274
275     ##
276     # Return the record in the form of a dictionary
277
278     def as_dict(self):
279         """
280         Return the record in the form of a dictionary
281         """
282         return dict(self)
283
284     ##
285     # Load the record from a dictionary
286     #
287     # @param dict dictionary to load record public from
288
289     def load_from_dict(self, dict):
290         """
291         Load the record from a dictionary 
292         """
293
294         self.set_name(dict['hrn'])
295         gidstr = dict.get("gid", None)
296         if gidstr:
297             self.set_gid(dict['gid'])
298
299         if "pointer" in dict:
300            self.set_pointer(dict['pointer'])
301
302         self.set_type(dict['type'])
303         self.update(dict)        
304     
305     ##
306     # Save the record to a string. The string contains an XML representation of
307     # the record.
308
309     def save_to_string(self):
310         """
311         Save the record to a string. The string contains an XML representation of
312         the record.
313         """
314         recorddict = self.as_dict()
315         filteredDict = dict([(key, val) for (key, val) in recorddict.iteritems() if key in self.fields.keys()])
316         xml_record = XML('<record/>')
317         xml_record.parse_dict(filteredDict)
318         str = xml_record.toxml()
319         return str
320
321     ##
322     # Load the record from a string. The string is assumed to contain an XML
323     # representation of the record.
324
325     def load_from_string(self, str):
326         """
327         Load the record from a string. The string is assumed to contain an XML
328         representation of the record.
329         """
330         #dict = xmlrpclib.loads(str)[0][0]
331
332         xml_record = XML(str)
333         self.load_from_dict(xml_record.todict())
334
335     ##
336     # Dump the record to stdout
337     #
338     # @param dump_parents if true, then the parents of the GID will be dumped
339
340     def dump(self, dump_parents=False):
341         """
342         Walk tree and dump records.
343         """
344         #print "RECORD", self.name
345         #print "        hrn:", self.name
346         #print "       type:", self.type
347         #print "        gid:"
348         #if (not self.gid):
349         #    print "        None"
350         #else:
351         #    self.get_gid_object().dump(8, dump_parents)
352         #print "    pointer:", self.pointer
353        
354         order = SfaRecord.fields.keys() 
355         for key in self.keys():
356             if key not in order:
357                 order.append(key)
358         for key in order:
359             if key in self and key in self.fields:
360                 if key in 'gid' and self[key]:
361                     gid = GID(string=self[key])
362                     print "     %s:" % key
363                     gid.dump(8, dump_parents)
364                 else:    
365                     print "     %s: %s" % (key, self[key])
366     
367     def summary_string(self):
368         return "Record(record_id=%s, hrn=%s, type=%s, authority=%s, pointer=%s)" % \
369                 (self.get('record_id'), self.get('hrn'), self.get('type'), self.get('authority'), \
370                  self.get('pointer'))
371
372     def getdict(self):
373         return dict(self)
374    
375     def sync(self):
376         """ 
377         Sync this record with the database.
378         """ 
379         from sfa.storage.table import SfaTable
380         table = SfaTable()
381         filter = {}
382         if self.get('record_id'):
383             filter['record_id'] = self.get('record_id')
384         if self.get('hrn') and self.get('type'):
385             filter['hrn'] = self.get('hrn') 
386             filter['type'] = self.get('type')
387             if self.get('pointer'):
388                 filter['pointer'] = self.get('pointer')
389         existing_records = table.find(filter)
390         if not existing_records:
391             table.insert(self)
392         else:
393             existing_record = existing_records[0]
394             self['record_id'] = existing_record['record_id']
395             table.update(self) 
396
397     def delete(self):
398         """
399         Remove record from the database.
400         """
401         from sfa.storage.table import SfaTable
402         table = SfaTable()
403         if self.get('record_id'):
404             filter['record_id'] = self.get('record_id')
405         if self.get('hrn') and self.get('type'):
406             filter['hrn'] = self.get('hrn')
407             filter['type'] = self.get('type')
408             if self.get('pointer'):
409                 filter['pointer'] = self.get('pointer')
410         existing_records = table.find(filter)
411         for record in existing_records:
412             table.remove(record)
413
414 class UserRecord(SfaRecord):
415
416     fields = {
417         'email': Parameter(str, 'email'),
418         'first_name': Parameter(str, 'First name'),
419         'last_name': Parameter(str, 'Last name'),
420         'phone': Parameter(str, 'Phone Number'),
421         'keys': Parameter(str, 'Public key'),
422         'slices': Parameter([str], 'List of slices this user belongs to'),
423         }
424     fields.update(SfaRecord.fields)
425     
426 class SliceRecord(SfaRecord):
427     fields = {
428         'name': Parameter(str, 'Slice name'),
429         'url': Parameter(str, 'Slice url'),
430         'expires': Parameter(int, 'Date and time this slice exipres'),
431         'researcher': Parameter([str], 'List of users for this slice'),
432         'PI': Parameter([str], 'List of PIs responsible for this slice'),
433         'description': Parameter([str], 'Description of this slice'), 
434         }
435     fields.update(SfaRecord.fields)
436
437  
438 class NodeRecord(SfaRecord):
439     fields = {
440         'hostname': Parameter(str, 'This nodes dns name'),
441         'node_type': Parameter(str, 'Type of node this is'),
442         'latitude': Parameter(str, 'latitude'),
443         'longitude': Parameter(str, 'longitude'),
444         }
445     fields.update(SfaRecord.fields)
446
447
448 class AuthorityRecord(SfaRecord):
449     fields =  {
450         'name': Parameter(str, 'Name'),
451         'login_base': Parameter(str, 'login base'),
452         'enabled': Parameter(bool, 'Is this site enabled'),
453         'url': Parameter(str, 'URL'),
454         'nodes': Parameter([str], 'List of nodes at this site'),  
455         'operator': Parameter([str], 'List of operators'),
456         'researcher': Parameter([str], 'List of researchers'),
457         'PI': Parameter([str], 'List of Principal Investigators'),
458         }
459     fields.update(SfaRecord.fields)
460     
461