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