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