3075edb8394099d75d8c9e7046c4c8eb73f7b8a9
[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         strs = []
233         for fieldname in fieldnames:
234             strs.append(self.get_field_value_string(fieldname))
235         return strs
236
237     ##
238     # Return the record in the form of a dictionary
239
240     def as_dict(self):
241         """
242         Return the record in the form of a dictionary
243         """
244         return dict(self)
245
246     ##
247     # Load the record from a dictionary
248     #
249     # @param dict dictionary to load record public from
250
251     def load_from_dict(self, dict):
252         """
253         Load the record from a dictionary 
254         """
255         self.set_name(dict['hrn'])
256         gidstr = dict.get("gid", None)
257         if gidstr:
258             self.set_gid(dict['gid'])
259
260         if "pointer" in dict:
261            self.set_pointer(dict['pointer'])
262
263         self.set_type(dict['type'])
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.fields.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.fields.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.fields):
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     fields = {
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     fields.update(GeniRecord.fields)
347     
348 class SliceRecord(GeniRecord):
349     fields = {
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     fields.update(GeniRecord.fields)
357
358  
359 class NodeRecord(GeniRecord):
360     fields = {
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     fields.update(GeniRecord.fields)
368
369
370 class AuthorityRecord(GeniRecord):
371     fields =  {
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     fields.update(GeniRecord.fields)
382     
383