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