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