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