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