6cb9d37d198ba0daded0584d502ea302cbe3c03a
[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.namespace 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 a list of field names in this record. 
212
213     def get_field_names(self):
214         """
215         Returns a list of field names in this record.
216         """
217         return self.fields.keys()
218
219     ##
220     # Given a field name ("hrn", "gid", ...) return the value of that field.
221     #
222     # @param fieldname is the name of field to be returned
223
224     def get_field_value_string(self, fieldname):
225         """
226         Given a field name ("hrn", "gid", ...) return the value of that field.
227         """
228         if fieldname == "authority":
229             val = get_authority(self['hrn'])
230         else:
231             try:
232                 val = getattr(self, fieldname)
233             except:
234                 val = self[fieldname] 
235         if isinstance(val, str):
236             return "'" + str(val) + "'"
237         else:
238             return str(val)
239
240     ##
241     # Given a list of field names, return a list of values for those public.
242     #
243     # @param fieldnames is a list of field names
244
245     def get_field_value_strings(self, fieldnames):
246         """
247         Given a list of field names, return a list of values for those public.
248         """
249         return [ self.get_field_value_string (fieldname) for fieldname in fieldnames ]
250
251     ##
252     # Return the record in the form of a dictionary
253
254     def as_dict(self):
255         """
256         Return the record in the form of a dictionary
257         """
258         return dict(self)
259
260     ##
261     # Load the record from a dictionary
262     #
263     # @param dict dictionary to load record public from
264
265     def load_from_dict(self, dict):
266         """
267         Load the record from a dictionary 
268         """
269         self.set_name(dict['hrn'])
270         gidstr = dict.get("gid", None)
271         if gidstr:
272             self.set_gid(dict['gid'])
273
274         if "pointer" in dict:
275            self.set_pointer(dict['pointer'])
276
277         self.set_type(dict['type'])
278         self.update(dict)        
279     
280     ##
281     # Save the record to a string. The string contains an XML representation of
282     # the record.
283
284     def save_to_string(self):
285         """
286         Save the record to a string. The string contains an XML representation of
287         the record.
288         """
289         recorddict = self.as_dict()
290         filteredDict = dict([(key, val) for (key, val) in recorddict.iteritems() if key in self.fields.keys()])
291         record = RecordSpec()
292         record.parseDict(filteredDict)
293         str = record.toxml()
294         #str = xmlrpclib.dumps((dict,), allow_none=True)
295         return str
296
297     ##
298     # Load the record from a string. The string is assumed to contain an XML
299     # representation of the record.
300
301     def load_from_string(self, str):
302         """
303         Load the record from a string. The string is assumed to contain an XML
304         representation of the record.
305         """
306         #dict = xmlrpclib.loads(str)[0][0]
307         
308         record = RecordSpec()
309         record.parseString(str)
310         record_dict = record.toDict()
311         sfa_dict = record_dict['record']
312         self.load_from_dict(sfa_dict)
313
314     ##
315     # Dump the record to stdout
316     #
317     # @param dump_parents if true, then the parents of the GID will be dumped
318
319     def dump(self, dump_parents=False):
320         """
321         Walk tree and dump records.
322         """
323         #print "RECORD", self.name
324         #print "        hrn:", self.name
325         #print "       type:", self.type
326         #print "        gid:"
327         #if (not self.gid):
328         #    print "        None"
329         #else:
330         #    self.get_gid_object().dump(8, dump_parents)
331         #print "    pointer:", self.pointer
332        
333         order = SfaRecord.fields.keys() 
334         for key in self.keys():
335             if key not in order:
336                 order.append(key)
337         for key in order:
338             if key in self and key in self.fields:
339                 if key in 'gid' and self[key]:
340                     gid = GID(string=self[key])
341                     print "     %s:" % key
342                     gid.dump(8, dump_parents)
343                 else:    
344                     print "     %s: %s" % (key, self[key])
345     
346     def getdict(self):
347         return dict(self)
348     
349
350 class UserRecord(SfaRecord):
351
352     fields = {
353         'email': Parameter(str, 'email'),
354         'first_name': Parameter(str, 'First name'),
355         'last_name': Parameter(str, 'Last name'),
356         'phone': Parameter(str, 'Phone Number'),
357         'keys': Parameter(str, 'Public key'),
358         'slices': Parameter([str], 'List of slices this user belongs to'),
359         }
360     fields.update(SfaRecord.fields)
361     
362 class SliceRecord(SfaRecord):
363     fields = {
364         'name': Parameter(str, 'Slice name'),
365         'url': Parameter(str, 'Slice url'),
366         'expires': Parameter(int, 'Date and time this slice exipres'),
367         'researcher': Parameter([str], 'List of users for this slice'),
368         'PI': Parameter([str], 'List of PIs responsible for this slice'),
369         'description': Parameter([str], 'Description of this slice'), 
370         }
371     fields.update(SfaRecord.fields)
372
373  
374 class NodeRecord(SfaRecord):
375     fields = {
376         'hostname': Parameter(str, 'This nodes dns name'),
377         'node_type': Parameter(str, 'Type of node this is'),
378         'node_type': Parameter(str, 'Type of node this is'),
379         'latitude': Parameter(str, 'latitude'),
380         'longitude': Parameter(str, 'longitude'),
381         }
382     fields.update(SfaRecord.fields)
383
384
385 class AuthorityRecord(SfaRecord):
386     fields =  {
387         'name': Parameter(str, 'Name'),
388         'login_base': Parameter(str, 'login base'),
389         'enabled': Parameter(bool, 'Is this site enabled'),
390         'url': Parameter(str, 'URL'),
391         'nodes': Parameter([str], 'List of nodes at this site'),  
392         'operator': Parameter([str], 'List of operators'),
393         'researcher': Parameter([str], 'List of researchers'),
394         'PI': Parameter([str], 'List of Principal Investigators'),
395         }
396     fields.update(SfaRecord.fields)
397     
398