use sfa.util.xml.XML instead of sfa.util.rspec.RecordSpec
[sfa.git] / sfa / util / record.py
1 ##
2 # Implements support for SFA 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 from sfa.util.parameter import *
15 from sfa.util.xrn import get_authority
16 from sfa.util.row import Row
17 from sfa.util.xml import XML 
18
19 class SfaRecord(Row):
20     """ 
21     The SfaRecord class implements an SFA Record. A SfaRecord is a tuple
22     (Hrn, GID, Type, Info).
23  
24     Hrn specifies the Human Readable Name of the object
25     GID is the GID of the object
26     Type is user | authority | slice | component
27  
28     Info is comprised of the following sub-fields
29            pointer = a pointer to the record in the PL database
30  
31     The pointer is interpreted depending on the type of the record. For example,
32     if the type=="user", then pointer is assumed to be a person_id that indexes
33     into the persons table.
34  
35     A given HRN may have more than one record, provided that the records are
36     of different types.
37     """
38
39     table_name = 'sfa'
40     
41     primary_key = 'record_id'
42
43     ### the wsdl generator assumes this is named 'fields'
44     internal_fields = {
45         'record_id': Parameter(int, 'An id that uniquely identifies this record', ro=True),
46         'pointer': Parameter(int, 'An id that uniquely identifies this record in an external database ')
47     }
48
49     fields = {
50         'authority': Parameter(str, "The authority for this record"),
51         'peer_authority': Parameter(str, "The peer authority for this record"),
52         'hrn': Parameter(str, "Human readable name of object"),
53         'gid': Parameter(str, "GID of the object"),
54         'type': Parameter(str, "Record type"),
55         'last_updated': Parameter(int, 'Date and time of last update', ro=True),
56         'date_created': Parameter(int, 'Date and time this record was created', ro=True),
57     }
58     all_fields = dict(fields.items() + internal_fields.items())
59     ##
60     # Create an SFA Record
61     #
62     # @param name if !=None, assign the name of the record
63     # @param gid if !=None, assign the gid of the record
64     # @param type one of user | authority | slice | component
65     # @param pointer is a pointer to a PLC record
66     # @param dict if !=None, then fill in this record from the dictionary
67
68     def __init__(self, hrn=None, gid=None, type=None, pointer=None, peer_authority=None, dict=None, string=None):
69         self.dirty = True
70         self.hrn = None
71         self.gid = None
72         self.type = None
73         self.pointer = None
74         self.set_peer_auth(peer_authority)
75         if hrn:
76             self.set_name(hrn)
77         if gid:
78             self.set_gid(gid)
79         if type:
80             self.set_type(type)
81         if pointer:
82             self.set_pointer(pointer)
83         if dict:
84             self.load_from_dict(dict)
85         if string:
86             self.load_from_string(string)
87
88
89     def validate_last_updated(self, last_updated):
90         return time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime())
91         
92     def update(self, new_dict):
93         if isinstance(new_dict, list):
94             new_dict = new_dict[0]
95
96         # Convert any boolean strings to real bools
97         for key in new_dict:
98             if isinstance(new_dict[key], StringTypes):
99                 if new_dict[key].lower() in ["true"]:
100                     new_dict[key] = True
101                 elif new_dict[key].lower() in ["false"]:
102                     new_dict[key] = False
103         dict.update(self, new_dict)
104
105     ##
106     # Set the name of the record
107     #
108     # @param hrn is a string containing the HRN
109
110     def set_name(self, hrn):
111         """
112         Set the name of the record
113         """
114         self.hrn = hrn
115         self['hrn'] = hrn
116         self.dirty = True
117
118     ##
119     # Set the GID of the record
120     #
121     # @param gid is a GID object or the string representation of a GID object
122
123     def set_gid(self, gid):
124         """
125         Set the GID of the record
126         """
127
128         if isinstance(gid, StringTypes):
129             self.gid = gid
130             self['gid'] = gid
131         else:
132             self.gid = gid.save_to_string(save_parents=True)
133             self['gid'] = gid.save_to_string(save_parents=True)
134         self.dirty = True
135
136     ##
137     # Set the type of the record
138     #
139     # @param type is a string: user | authority | slice | component
140
141     def set_type(self, type):
142         """
143         Set the type of the record
144         """
145         self.type = type
146         self['type'] = type
147         self.dirty = True
148
149     ##
150     # Set the pointer of the record
151     #
152     # @param pointer is an integer containing the ID of a PLC record
153
154     def set_pointer(self, pointer):
155         """
156         Set the pointer of the record
157         """
158         self.pointer = pointer
159         self['pointer'] = pointer
160         self.dirty = True
161
162
163     def set_peer_auth(self, peer_authority):
164         self.peer_authority = peer_authority
165         self['peer_authority'] = peer_authority
166         self.dirty = True
167
168     ##
169     # Return the name (HRN) of the record
170
171     def get_name(self):
172         """
173         Return the name (HRN) of the record
174         """
175         return self.hrn
176
177     ##
178     # Return the type of the record
179
180     def get_type(self):
181         """
182         Return the type of the record
183         """
184         return self.type
185
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     def get_pointer(self):
192         """
193         Return the pointer of the record. The pointer is an integer that may be
194         used to look up the record in the PLC database. The evaluation of pointer
195         depends on the type of the record
196         """
197         return self.pointer
198
199     ##
200     # Return the GID of the record, in the form of a GID object
201     # TODO: not the best name for the function, because we have things called
202     # gidObjects in the Cred
203
204     def get_gid_object(self):
205         """
206         Return the GID of the record, in the form of a GID object
207         """
208         return GID(string=self.gid)
209
210     ##
211     # Returns the value of a field
212
213     def get_field(self, fieldname, default=None):
214         # sometimes records act like classes, and sometimes they act like dicts
215         try:
216             return getattr(self, fieldname)
217         except AttributeError:
218             try:
219                  return self[fieldname]
220             except KeyError:
221                  if default != None:
222                      return default
223                  else:
224                      raise
225
226     ##
227     # Returns a list of field names in this record. 
228
229     def get_field_names(self):
230         """
231         Returns a list of field names in this record.
232         """
233         return self.fields.keys()
234
235     ##
236     # Given a field name ("hrn", "gid", ...) return the value of that field.
237     #
238     # @param fieldname is the name of field to be returned
239
240     def get_field_value_string(self, fieldname):
241         """
242         Given a field name ("hrn", "gid", ...) return the value of that field.
243         """
244         if fieldname == "authority":
245             val = get_authority(self['hrn'])
246         else:
247             try:
248                 val = getattr(self, fieldname)
249             except:
250                 val = self[fieldname] 
251         if isinstance(val, str):
252             return "'" + str(val) + "'"
253         else:
254             return str(val)
255
256     ##
257     # Given a list of field names, return a list of values for those public.
258     #
259     # @param fieldnames is a list of field names
260
261     def get_field_value_strings(self, fieldnames):
262         """
263         Given a list of field names, return a list of values for those public.
264         """
265         return [ self.get_field_value_string (fieldname) for fieldname in fieldnames ]
266
267     ##
268     # Return the record in the form of a dictionary
269
270     def as_dict(self):
271         """
272         Return the record in the form of a dictionary
273         """
274         return dict(self)
275
276     ##
277     # Load the record from a dictionary
278     #
279     # @param dict dictionary to load record public from
280
281     def load_from_dict(self, dict):
282         """
283         Load the record from a dictionary 
284         """
285         self.set_name(dict['hrn'])
286         gidstr = dict.get("gid", None)
287         if gidstr:
288             self.set_gid(dict['gid'])
289
290         if "pointer" in dict:
291            self.set_pointer(dict['pointer'])
292
293         self.set_type(dict['type'])
294         self.update(dict)        
295     
296     ##
297     # Save the record to a string. The string contains an XML representation of
298     # the record.
299
300     def save_to_string(self):
301         """
302         Save the record to a string. The string contains an XML representation of
303         the record.
304         """
305         recorddict = self.as_dict()
306         filteredDict = dict([(key, val) for (key, val) in recorddict.iteritems() if key in self.fields.keys()])
307         record = XML('<record/>')
308         record.root.attrib.update(filteredDict)
309         str = record.toxml()
310         return str
311
312     ##
313     # Load the record from a string. The string is assumed to contain an XML
314     # representation of the record.
315
316     def load_from_string(self, str):
317         """
318         Load the record from a string. The string is assumed to contain an XML
319         representation of the record.
320         """
321         #dict = xmlrpclib.loads(str)[0][0]
322         
323         record = XML(str)
324         self.load_from_dict(record.root.attrib)
325
326     ##
327     # Dump the record to stdout
328     #
329     # @param dump_parents if true, then the parents of the GID will be dumped
330
331     def dump(self, dump_parents=False):
332         """
333         Walk tree and dump records.
334         """
335         #print "RECORD", self.name
336         #print "        hrn:", self.name
337         #print "       type:", self.type
338         #print "        gid:"
339         #if (not self.gid):
340         #    print "        None"
341         #else:
342         #    self.get_gid_object().dump(8, dump_parents)
343         #print "    pointer:", self.pointer
344        
345         order = SfaRecord.fields.keys() 
346         for key in self.keys():
347             if key not in order:
348                 order.append(key)
349         for key in order:
350             if key in self and key in self.fields:
351                 if key in 'gid' and self[key]:
352                     gid = GID(string=self[key])
353                     print "     %s:" % key
354                     gid.dump(8, dump_parents)
355                 else:    
356                     print "     %s: %s" % (key, self[key])
357     
358     def getdict(self):
359         return dict(self)
360     
361
362 class UserRecord(SfaRecord):
363
364     fields = {
365         'email': Parameter(str, 'email'),
366         'first_name': Parameter(str, 'First name'),
367         'last_name': Parameter(str, 'Last name'),
368         'phone': Parameter(str, 'Phone Number'),
369         'keys': Parameter(str, 'Public key'),
370         'slices': Parameter([str], 'List of slices this user belongs to'),
371         }
372     fields.update(SfaRecord.fields)
373     
374 class SliceRecord(SfaRecord):
375     fields = {
376         'name': Parameter(str, 'Slice name'),
377         'url': Parameter(str, 'Slice url'),
378         'expires': Parameter(int, 'Date and time this slice exipres'),
379         'researcher': Parameter([str], 'List of users for this slice'),
380         'PI': Parameter([str], 'List of PIs responsible for this slice'),
381         'description': Parameter([str], 'Description of this slice'), 
382         }
383     fields.update(SfaRecord.fields)
384
385  
386 class NodeRecord(SfaRecord):
387     fields = {
388         'hostname': Parameter(str, 'This nodes dns name'),
389         'node_type': Parameter(str, 'Type of node this is'),
390         'node_type': Parameter(str, 'Type of node this is'),
391         'latitude': Parameter(str, 'latitude'),
392         'longitude': Parameter(str, 'longitude'),
393         }
394     fields.update(SfaRecord.fields)
395
396
397 class AuthorityRecord(SfaRecord):
398     fields =  {
399         'name': Parameter(str, 'Name'),
400         'login_base': Parameter(str, 'login base'),
401         'enabled': Parameter(bool, 'Is this site enabled'),
402         'url': Parameter(str, 'URL'),
403         'nodes': Parameter([str], 'List of nodes at this site'),  
404         'operator': Parameter([str], 'List of operators'),
405         'researcher': Parameter([str], 'List of researchers'),
406         'PI': Parameter([str], 'List of Principal Investigators'),
407         }
408     fields.update(SfaRecord.fields)
409     
410