moved plc specific code out of sfa.importer.sfaImporter. Refactored sfa.importer...
[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     
37     primary_key = 'record_id'
38
39     ### the wsdl generator assumes this is named 'fields'
40     internal_fields = {
41         'record_id': Parameter(int, 'An id that uniquely identifies this record', ro=True),
42         'pointer': Parameter(int, 'An id that uniquely identifies this record in an external database ')
43     }
44
45     fields = {
46         'authority': Parameter(str, "The authority for this record"),
47         'peer_authority': Parameter(str, "The peer authority for this record"),
48         'hrn': Parameter(str, "Human readable name of object"),
49         'gid': Parameter(str, "GID of the object"),
50         'type': Parameter(str, "Record type"),
51         'last_updated': Parameter(int, 'Date and time of last update', ro=True),
52         'date_created': Parameter(int, 'Date and time this record was created', ro=True),
53     }
54     all_fields = dict(fields.items() + internal_fields.items())
55     ##
56     # Create an SFA Record
57     #
58     # @param name if !=None, assign the name of the record
59     # @param gid if !=None, assign the gid of the record
60     # @param type one of user | authority | slice | component
61     # @param pointer is a pointer to a PLC record
62     # @param dict if !=None, then fill in this record from the dictionary
63
64     def __init__(self, hrn=None, gid=None, type=None, pointer=None, authority=None, 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         record = XML('<record/>')
317         record.parse_dict(filteredDict)
318         str = 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         record = XML(str)
333         self.load_from_dict(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, auth=%s, pointer=%s)" % \
369                 (self.get('record_id'), self.get('hrn'), self.get('type'), self.get('auth'), \
370                  self.get('pointer'))
371
372     def getdict(self):
373         return dict(self)
374    
375     def sync(self, verbose=False):
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             if verbose:
393                 logger.info("Inserted record: %s" %self.summary_string()) 
394         else:
395             existing_record = existing_records[0]
396             self['record_id'] = existing_record['record_id']
397             table.upate(self) 
398             if verbose:
399                 logger.info("Updated record: %s" % self.summary_string()) 
400
401     def delete(self, verbose=False):
402         """
403         Remove record from the database.
404         """
405         from sfa.storage.table import SfaTable
406         table = SfaTable()
407         if self.get('record_id'):
408             filter['record_id'] = self.get('record_id')
409         if self.get('hrn') and self.get('type'):
410             filter['hrn'] = self.get('hrn')
411             filter['type'] = self.get('type')
412             if self.get('pointer'):
413                 filter['pointer'] = self.get('pointer')
414         existing_records = table.find(filter)
415         for record in existing_records:
416             table.remove(record)
417             if verbose:
418                 logger.info("Removed record: %s" % self.summary_string())    
419
420 class UserRecord(SfaRecord):
421
422     fields = {
423         'email': Parameter(str, 'email'),
424         'first_name': Parameter(str, 'First name'),
425         'last_name': Parameter(str, 'Last name'),
426         'phone': Parameter(str, 'Phone Number'),
427         'keys': Parameter(str, 'Public key'),
428         'slices': Parameter([str], 'List of slices this user belongs to'),
429         }
430     fields.update(SfaRecord.fields)
431     
432 class SliceRecord(SfaRecord):
433     fields = {
434         'name': Parameter(str, 'Slice name'),
435         'url': Parameter(str, 'Slice url'),
436         'expires': Parameter(int, 'Date and time this slice exipres'),
437         'researcher': Parameter([str], 'List of users for this slice'),
438         'PI': Parameter([str], 'List of PIs responsible for this slice'),
439         'description': Parameter([str], 'Description of this slice'), 
440         }
441     fields.update(SfaRecord.fields)
442
443  
444 class NodeRecord(SfaRecord):
445     fields = {
446         'hostname': Parameter(str, 'This nodes dns name'),
447         'node_type': Parameter(str, 'Type of node this is'),
448         'node_type': Parameter(str, 'Type of node this is'),
449         'latitude': Parameter(str, 'latitude'),
450         'longitude': Parameter(str, 'longitude'),
451         }
452     fields.update(SfaRecord.fields)
453
454
455 class AuthorityRecord(SfaRecord):
456     fields =  {
457         'name': Parameter(str, 'Name'),
458         'login_base': Parameter(str, 'login base'),
459         'enabled': Parameter(bool, 'Is this site enabled'),
460         'url': Parameter(str, 'URL'),
461         'nodes': Parameter([str], 'List of nodes at this site'),  
462         'operator': Parameter([str], 'List of operators'),
463         'researcher': Parameter([str], 'List of researchers'),
464         'PI': Parameter([str], 'List of Principal Investigators'),
465         }
466     fields.update(SfaRecord.fields)
467     
468