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