## # Implements support for geni records # # TODO: Use existing PLC database methods? or keep this separate? ## ### $Id$ ### $URL$ from types import StringTypes from sfa.trust.gid import * import sfa.util.report from sfa.util.rspec import * from sfa.util.parameter import * class GeniRecord(dict): """ The GeniRecord class implements a Geni Record. A GeniRecord is a tuple (Name, GID, Type, Info). Name specifies the HRN of the object GID is the GID of the object Type is user | authority | slice | component Info is comprised of the following sub-fields pointer = a pointer to the record in the PL database The pointer is interpreted depending on the type of the record. For example, if the type=="user", then pointer is assumed to be a person_id that indexes into the persons table. A given HRN may have more than one record, provided that the records are of different types. """ fields = { 'hrn': Parameter(str, "Human readable name of object"), 'gid': Parameter(str, "GID of the object"), 'type': Parameter(str, "Record type"), #'last_updated': Parameter(int, 'Date and time of last update'), #'date_created': Parameter(int, 'Date and time this record was created'), } internal_fields = { 'pointer': Parameter(int, "Internal ID") } ## # Create a Geni Record # # @param name if !=None, assign the name of the record # @param gid if !=None, assign the gid of the record # @param type one of user | authority | slice | component # @param pointer is a pointer to a PLC record # @param dict if !=None, then fill in this record from the dictionary def __init__(self, name=None, gid=None, type=None, pointer=None, dict=None, string=None): self.dirty = True self.name = None self.gid = None self.type = None self.pointer = None if name: self.set_name(name) if gid: self.set_gid(gid) if type: self.set_type(type) if pointer: self.set_pointer(pointer) if dict: self.load_from_dict(dict) if string: self.load_from_string(string) def update(self, new_dict): if isinstance(new_dict, list): new_dict = new_dict[0] # Convert any boolean strings to real bools for key in new_dict: if isinstance(new_dict[key], StringTypes): if new_dict[key].lower() in ["true"]: new_dict[key] = True elif new_dict[key].lower() in ["false"]: new_dict[key] = False dict.update(self, new_dict) ## # Set the name of the record # # @param name is a string containing the HRN def set_name(self, name): """ Set the name of the record """ self.name = name self.dirty = True ## # Set the GID of the record # # @param gid is a GID object or the string representation of a GID object def set_gid(self, gid): """ Set the GID of the record """ if isinstance(gid, StringTypes): self.gid = gid else: self.gid = gid.save_to_string(save_parents=True) self.dirty = True ## # Set the type of the record # # @param type is a string: user | authority | slice | component def set_type(self, type): """ Set the type of the record """ self.type = type self.dirty = True ## # Set the pointer of the record # # @param pointer is an integer containing the ID of a PLC record def set_pointer(self, pointer): """ Set the pointer of the record """ self.pointer = pointer self.dirty = True ## # Return the name (HRN) of the record def get_name(self): """ Return the name (HRN) of the record """ return self.name ## # Return the type of the record def get_type(self): """ Return the type of the record """ return self.type ## # Return the pointer of the record. The pointer is an integer that may be # used to look up the record in the PLC database. The evaluation of pointer # depends on the type of the record def get_pointer(self): """ Return the pointer of the record. The pointer is an integer that may be used to look up the record in the PLC database. The evaluation of pointer depends on the type of the record """ return self.pointer ## # Return the GID of the record, in the form of a GID object # TODO: not the best name for the function, because we have things called # gidObjects in the Cred def get_gid_object(self): """ Return the GID of the record, in the form of a GID object """ return GID(string=self.gid) ## # Return a key that uniquely identifies this record among all records in # Geni. This key is used to uniquely identify the record in the Geni # database. def get_key(self): """ Return a key that uniquely identifies this record among all records in Geni. This key is used to uniquely identify the record in the Geni database. """ return self.name + "#" + self.type ## # Returns a list of field names in this record. def get_field_names(self): """ Returns a list of field names in this record. """ return ["name", "gid", "type", "pointer"] ## # Given a field name ("name", "gid", ...) return the value of that field. # # @param name is the name of field to be returned def get_field_value_string(self, fieldname): """ Given a field name ("name", "gid", ...) return the value of that field. """ if fieldname == "key": val = self.get_key() else: val = getattr(self, fieldname) if isinstance(val, str): return "'" + str(val) + "'" else: return str(val) ## # Given a list of field names, return a list of values for those fields. # # @param fieldnames is a list of field names def get_field_value_strings(self, fieldnames): """ Given a list of field names, return a list of values for those fields. """ strs = [] for fieldname in fieldnames: strs.append(self.get_field_value_string(fieldname)) return strs ## # Return the record in the form of a dictionary def as_dict(self): """ Return the record in the form of a dictionary """ return dict(self) ## # Load the record from a dictionary # # @param dict dictionary to load record fields from def load_from_dict(self, dict): """ Load the record from a dictionary """ self.set_name(dict['name']) gidstr = dict.get("gid", None) if gidstr: self.set_gid(dict['gid']) if "pointer" in dict: self.set_pointer(dict['pointer']) self.set_type(dict['type']) self['hrn'] = dict['name'] self.update(dict) ## # Save the record to a string. The string contains an XML representation of # the record. def save_to_string(self): """ Save the record to a string. The string contains an XML representation of the record. """ dict = self.as_dict() record = RecordSpec() record.parseDict(dict) str = record.toxml() #str = xmlrpclib.dumps((dict,), allow_none=True) return str ## # Load the record from a string. The string is assumed to contain an XML # representation of the record. def load_from_string(self, str): """ Load the record from a string. The string is assumed to contain an XML representation of the record. """ #dict = xmlrpclib.loads(str)[0][0] record = RecordSpec() record.parseString(str) record_dict = record.toDict() geni_dict = record_dict['record'] self.load_from_dict(geni_dict) ## # Dump the record to stdout # # @param dump_parents if true, then the parents of the GID will be dumped def dump(self, dump_parents=False): """ Walk tree and dump records. """ #print "RECORD", self.name #print " hrn:", self.name #print " type:", self.type #print " gid:" #if (not self.gid): # print " None" #else: # self.get_gid_object().dump(8, dump_parents) #print " pointer:", self.pointer order = GeniRecord.fields.keys() for key in self.keys(): if key not in order: order.append(key) for key in order: if key in (self and self.fields): if key in 'gid' and self[key]: gid = GID(string=self[key]) print " %s:" % key gid.dump(8, dump_parents) else: print " %s: %s" % (key, self[key]) def getdict(self): return dict(self) class UserRecord(GeniRecord): fields = { 'email': Parameter(str, 'email'), 'first_name': Parameter(str, 'First name'), 'last_name': Parameter(str, 'Last name'), 'phone': Parameter(str, 'Phone Number'), 'key': Parameter(str, 'Public key'), 'slice': Parameter([str], 'List of slices this user belongs to'), } fields.update(GeniRecord.fields) internal_fields = { 'roles': Parameter([str], 'List of roles'), 'title': Parameter(str, 'Title'), 'sites': Parameter([str], 'List of sites this user belongs to'), 'enabled': Parameter(bool, 'Is this person enabled'), } internal_fields.update(GeniRecord.internal_fields) class SliceRecord(GeniRecord): fields = { 'name': Parameter(str, 'Slice name'), 'url': Parameter(str, 'Slice url'), 'expires': Parameter(int, 'Date and time this slice exipres'), 'researcher': Parameter([str], 'List of users for this slice'), 'description': Parameter([str], 'Description of this slice'), } fields.update(GeniRecord.fields) internal_fields = { 'site': Parameter(str, 'Site this slice belongs to'), 'instantiation': Parameter(str, 'Slice instantiation'), 'nodes': Parameter([str], 'List of nodes this slice is instantiated on'), 'max_nodes': Parameter(int, 'Maximum number of nodes this slice is allowed on') } internal_fields.update(GeniRecord.internal_fields) class NodeRecord(GeniRecord): fields = { 'hostname': Parameter(str, 'This nodes dns name'), 'node_type': Parameter(str, 'Type of node this is'), 'node_type': Parameter(str, 'Type of node this is'), 'latitude': Parameter(str, 'latitude'), 'longitude': Parameter(str, 'longitude'), } fields.update(GeniRecord.fields) internal_fields = { 'slice_ids_whitelist': Parameter([str], 'List of allowed slices on this node'), 'site': Parameter(str, 'Site this node belongs to'), 'slices': Parameter([str], 'List of instantiated slices on this node'), 'boot_state': Parameter(str, 'This nodes boot state'), 'session': Parameter(str, 'This nodes session key'), 'ssh_rsa_key': Parameter(str, 'Last known ssh host key'), 'verified': Parameter(str, 'Whether the node configuration is verified correct'), 'last_contact': Parameter(int, 'Date and time this node last phoned home'), 'run_level': Parameter(str, 'Run level'), 'version': Parameter(str, 'Node software version'), 'key': Parameter(str, 'Node key'), 'boot_noonce': Parameter(str, 'Random value generate at nodes last boot'), 'model': Parameter(str, 'Model of node'), 'ports': Parameter([int], 'List of pcu ports this node is connected to') } internal_fields.update(GeniRecord.internal_fields) class AuthorityRecord(GeniRecord): fields = { 'name': Parameter(str, 'Name'), 'login_base': Parameter(str, 'login base'), 'enabled': Parameter(bool, 'Is this site enabled'), 'url': Parameter(str, 'URL'), 'nodes': Parameter([str], 'List of nodes at this site'), 'operator': Parameter([str], 'List of operators'), 'researcher': Parameter([str], 'List of researchers'), 'PI': Parameter([str], 'List of Principal Investigators'), } fields.update(GeniRecord.fields) internal_fields = { 'nodes': Parameter([str], 'List of nodes at this site'), 'slices': Parameter([str], 'List of slices instantiated by this site'), 'abbreviated_name': Parameter(str, 'Abbreviated name'), 'owners': Parameter([str], 'List of owners'), 'max_slices': Parameter(int, 'Maximum number of slices this site can instantiate'), 'max_slivers': Parameter(int, 'Maximum number of slivers this site can instantiate'), 'pi': Parameter([str], 'List of pis'), 'is_public': Parameter(bool, 'Is this site public'), } internal_fields.update(GeniRecord.internal_fields)