changed the sfa db schema. All records are now stored in 1 table instead of createing...
[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         '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'),
52         'date_created': Parameter(int, 'Date and time this record was created'),
53     }
54     all_fields = dict(fields.items() + internal_fields.items())
55     ##
56     # Create a Geni 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, 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         if hrn:
71             self.set_name(hrn)
72         if gid:
73             self.set_gid(gid)
74         if type:
75             self.set_type(type)
76         if pointer:
77             self.set_pointer(pointer)
78         if dict:
79             self.load_from_dict(dict)
80         if string:
81             self.load_from_string(string)
82         
83     def update(self, new_dict):
84         if isinstance(new_dict, list):
85             new_dict = new_dict[0]
86
87         # Convert any boolean strings to real bools
88         for key in new_dict:
89             if isinstance(new_dict[key], StringTypes):
90                 if new_dict[key].lower() in ["true"]:
91                     new_dict[key] = True
92                 elif new_dict[key].lower() in ["false"]:
93                     new_dict[key] = False
94         dict.update(self, new_dict)
95
96     ##
97     # Set the name of the record
98     #
99     # @param hrn is a string containing the HRN
100
101     def set_name(self, hrn):
102         """
103         Set the name of the record
104         """
105         self.hrn = hrn
106         self['hrn'] = hrn
107         self.dirty = True
108
109     ##
110     # Set the GID of the record
111     #
112     # @param gid is a GID object or the string representation of a GID object
113
114     def set_gid(self, gid):
115         """
116         Set the GID of the record
117         """
118
119         if isinstance(gid, StringTypes):
120             self.gid = gid
121             self['gid'] = gid
122         else:
123             self.gid = gid.save_to_string(save_parents=True)
124             self['gid'] = gid.save_to_string(save_parents=True)
125         self.dirty = True
126
127     ##
128     # Set the type of the record
129     #
130     # @param type is a string: user | authority | slice | component
131
132     def set_type(self, type):
133         """
134         Set the type of the record
135         """
136         self.type = type
137         self['type'] = type
138         self.dirty = True
139
140     ##
141     # Set the pointer of the record
142     #
143     # @param pointer is an integer containing the ID of a PLC record
144
145     def set_pointer(self, pointer):
146         """
147         Set the pointer of the record
148         """
149         self.pointer = pointer
150         self['pointer'] = pointer
151         self.dirty = True
152
153     ##
154     # Return the name (HRN) of the record
155
156     def get_name(self):
157         """
158         Return the name (HRN) of the record
159         """
160         return self.hrn
161
162     ##
163     # Return the type of the record
164
165     def get_type(self):
166         """
167         Return the type of the record
168         """
169         return self.type
170
171     ##
172     # Return the pointer of the record. The pointer is an integer that may be
173     # used to look up the record in the PLC database. The evaluation of pointer
174     # depends on the type of the record
175
176     def get_pointer(self):
177         """
178         Return the pointer of the record. The pointer is an integer that may be
179         used to look up the record in the PLC database. The evaluation of pointer
180         depends on the type of the record
181         """
182         return self.pointer
183
184     ##
185     # Return the GID of the record, in the form of a GID object
186     # TODO: not the best name for the function, because we have things called
187     # gidObjects in the Cred
188
189     def get_gid_object(self):
190         """
191         Return the GID of the record, in the form of a GID object
192         """
193         return GID(string=self.gid)
194
195     ##
196     # Returns a list of field names in this record. 
197
198     def get_field_names(self):
199         """
200         Returns a list of field names in this record.
201         """
202         return self.fields.keys()
203
204     ##
205     # Given a field name ("hrn", "gid", ...) return the value of that field.
206     #
207     # @param fieldname is the name of field to be returned
208
209     def get_field_value_string(self, fieldname):
210         """
211         Given a field name ("hrn", "gid", ...) return the value of that field.
212         """
213         if fieldname == "authority":
214             val = get_authority(self['hrn'])
215         else:
216             try:
217                 val = getattr(self, fieldname)
218             except:
219                 val = self[fieldname] 
220         if isinstance(val, str):
221             return "'" + str(val) + "'"
222         else:
223             return str(val)
224
225     ##
226     # Given a list of field names, return a list of values for those public.
227     #
228     # @param fieldnames is a list of field names
229
230     def get_field_value_strings(self, fieldnames):
231         """
232         Given a list of field names, return a list of values for those public.
233         """
234         return [ self.get_field_value_string (fieldname) for fieldname in fieldnames ]
235
236     ##
237     # Return the record in the form of a dictionary
238
239     def as_dict(self):
240         """
241         Return the record in the form of a dictionary
242         """
243         return dict(self)
244
245     ##
246     # Load the record from a dictionary
247     #
248     # @param dict dictionary to load record public from
249
250     def load_from_dict(self, dict):
251         """
252         Load the record from a dictionary 
253         """
254         self.set_name(dict['hrn'])
255         gidstr = dict.get("gid", None)
256         if gidstr:
257             self.set_gid(dict['gid'])
258
259         if "pointer" in dict:
260            self.set_pointer(dict['pointer'])
261
262         self.set_type(dict['type'])
263         self.update(dict)        
264     
265     ##
266     # Save the record to a string. The string contains an XML representation of
267     # the record.
268
269     def save_to_string(self):
270         """
271         Save the record to a string. The string contains an XML representation of
272         the record.
273         """
274         recorddict = self.as_dict()
275         filteredDict = dict([(key, val) for (key, val) in recorddict.iteritems() if key in self.fields.keys()])
276         record = RecordSpec()
277         record.parseDict(filteredDict)
278         str = record.toxml()
279         #str = xmlrpclib.dumps((dict,), allow_none=True)
280         return str
281
282     ##
283     # Load the record from a string. The string is assumed to contain an XML
284     # representation of the record.
285
286     def load_from_string(self, str):
287         """
288         Load the record from a string. The string is assumed to contain an XML
289         representation of the record.
290         """
291         #dict = xmlrpclib.loads(str)[0][0]
292         
293         record = RecordSpec()
294         record.parseString(str)
295         record_dict = record.toDict()
296         geni_dict = record_dict['record']
297         self.load_from_dict(geni_dict)
298
299     ##
300     # Dump the record to stdout
301     #
302     # @param dump_parents if true, then the parents of the GID will be dumped
303
304     def dump(self, dump_parents=False):
305         """
306         Walk tree and dump records.
307         """
308         #print "RECORD", self.name
309         #print "        hrn:", self.name
310         #print "       type:", self.type
311         #print "        gid:"
312         #if (not self.gid):
313         #    print "        None"
314         #else:
315         #    self.get_gid_object().dump(8, dump_parents)
316         #print "    pointer:", self.pointer
317        
318         order = GeniRecord.fields.keys() 
319         for key in self.keys():
320             if key not in order:
321                 order.append(key)
322         for key in order:
323             if key in self and key in self.fields:
324                 if key in 'gid' and self[key]:
325                     gid = GID(string=self[key])
326                     print "     %s:" % key
327                     gid.dump(8, dump_parents)
328                 else:    
329                     print "     %s: %s" % (key, self[key])
330     
331     def getdict(self):
332         return dict(self)
333     
334
335 class UserRecord(GeniRecord):
336
337     fields = {
338         'email': Parameter(str, 'email'),
339         'first_name': Parameter(str, 'First name'),
340         'last_name': Parameter(str, 'Last name'),
341         'phone': Parameter(str, 'Phone Number'),
342         'key': Parameter(str, 'Public key'),
343         'slices': Parameter([str], 'List of slices this user belongs to'),
344         }
345     fields.update(GeniRecord.fields)
346     
347 class SliceRecord(GeniRecord):
348     fields = {
349         'name': Parameter(str, 'Slice name'),
350         'url': Parameter(str, 'Slice url'),
351         'expires': Parameter(int, 'Date and time this slice exipres'),
352         'researcher': Parameter([str], 'List of users for this slice'),
353         'description': Parameter([str], 'Description of this slice'), 
354         }
355     fields.update(GeniRecord.fields)
356
357  
358 class NodeRecord(GeniRecord):
359     fields = {
360         'hostname': Parameter(str, 'This nodes dns name'),
361         'node_type': Parameter(str, 'Type of node this is'),
362         'node_type': Parameter(str, 'Type of node this is'),
363         'latitude': Parameter(str, 'latitude'),
364         'longitude': Parameter(str, 'longitude'),
365         }
366     fields.update(GeniRecord.fields)
367
368
369 class AuthorityRecord(GeniRecord):
370     fields =  {
371         'name': Parameter(str, 'Name'),
372         'login_base': Parameter(str, 'login base'),
373         'enabled': Parameter(bool, 'Is this site enabled'),
374         'url': Parameter(str, 'URL'),
375         'nodes': Parameter([str], 'List of nodes at this site'),  
376         'operator': Parameter([str], 'List of operators'),
377         'researcher': Parameter([str], 'List of researchers'),
378         'PI': Parameter([str], 'List of Principal Investigators'),
379         }
380     fields.update(GeniRecord.fields)
381     
382