move init.d and cron.d one step up in the source tree
[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
283         self.set_name(dict['hrn'])
284         gidstr = dict.get("gid", None)
285         if gidstr:
286             self.set_gid(dict['gid'])
287
288         if "pointer" in dict:
289            self.set_pointer(dict['pointer'])
290
291         self.set_type(dict['type'])
292         self.update(dict)        
293     
294     ##
295     # Save the record to a string. The string contains an XML representation of
296     # the record.
297
298     def save_to_string(self):
299         """
300         Save the record to a string. The string contains an XML representation of
301         the record.
302         """
303         recorddict = self.as_dict()
304         filteredDict = dict([(key, val) for (key, val) in recorddict.iteritems() if key in self.fields.keys()])
305         record = XML('<record/>')
306         record.parse_dict(filteredDict)
307         str = record.toxml()
308         return str
309
310     ##
311     # Load the record from a string. The string is assumed to contain an XML
312     # representation of the record.
313
314     def load_from_string(self, str):
315         """
316         Load the record from a string. The string is assumed to contain an XML
317         representation of the record.
318         """
319         #dict = xmlrpclib.loads(str)[0][0]
320
321         record = XML(str)
322         self.load_from_dict(record.todict())
323
324     ##
325     # Dump the record to stdout
326     #
327     # @param dump_parents if true, then the parents of the GID will be dumped
328
329     def dump(self, dump_parents=False):
330         """
331         Walk tree and dump records.
332         """
333         #print "RECORD", self.name
334         #print "        hrn:", self.name
335         #print "       type:", self.type
336         #print "        gid:"
337         #if (not self.gid):
338         #    print "        None"
339         #else:
340         #    self.get_gid_object().dump(8, dump_parents)
341         #print "    pointer:", self.pointer
342        
343         order = SfaRecord.fields.keys() 
344         for key in self.keys():
345             if key not in order:
346                 order.append(key)
347         for key in order:
348             if key in self and key in self.fields:
349                 if key in 'gid' and self[key]:
350                     gid = GID(string=self[key])
351                     print "     %s:" % key
352                     gid.dump(8, dump_parents)
353                 else:    
354                     print "     %s: %s" % (key, self[key])
355     
356     def getdict(self):
357         return dict(self)
358     
359
360 class UserRecord(SfaRecord):
361
362     fields = {
363         'email': Parameter(str, 'email'),
364         'first_name': Parameter(str, 'First name'),
365         'last_name': Parameter(str, 'Last name'),
366         'phone': Parameter(str, 'Phone Number'),
367         'keys': Parameter(str, 'Public key'),
368         'slices': Parameter([str], 'List of slices this user belongs to'),
369         }
370     fields.update(SfaRecord.fields)
371     
372 class SliceRecord(SfaRecord):
373     fields = {
374         'name': Parameter(str, 'Slice name'),
375         'url': Parameter(str, 'Slice url'),
376         'expires': Parameter(int, 'Date and time this slice exipres'),
377         'researcher': Parameter([str], 'List of users for this slice'),
378         'PI': Parameter([str], 'List of PIs responsible for this slice'),
379         'description': Parameter([str], 'Description of this slice'), 
380         }
381     fields.update(SfaRecord.fields)
382
383  
384 class NodeRecord(SfaRecord):
385     fields = {
386         'hostname': Parameter(str, 'This nodes dns name'),
387         'node_type': Parameter(str, 'Type of node this is'),
388         'node_type': Parameter(str, 'Type of node this is'),
389         'latitude': Parameter(str, 'latitude'),
390         'longitude': Parameter(str, 'longitude'),
391         }
392     fields.update(SfaRecord.fields)
393
394
395 class AuthorityRecord(SfaRecord):
396     fields =  {
397         'name': Parameter(str, 'Name'),
398         'login_base': Parameter(str, 'login base'),
399         'enabled': Parameter(bool, 'Is this site enabled'),
400         'url': Parameter(str, 'URL'),
401         'nodes': Parameter([str], 'List of nodes at this site'),  
402         'operator': Parameter([str], 'List of operators'),
403         'researcher': Parameter([str], 'List of researchers'),
404         'PI': Parameter([str], 'List of Principal Investigators'),
405         }
406     fields.update(SfaRecord.fields)
407     
408