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