X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=util%2Fgeniclient.py;h=accf658a7231c0fa3eeadcaf96675d878a2457da;hb=455f3a3cfa775a386a66db8ca0a040f7c5b33d29;hp=2de52e2ffdd8a29604b01553a8fe270c2d1a7e10;hpb=be4bba4d844ba251a7f5898fbe199d998f1ac283;p=sfa.git diff --git a/util/geniclient.py b/util/geniclient.py index 2de52e2f..accf658a 100644 --- a/util/geniclient.py +++ b/util/geniclient.py @@ -1,10 +1,10 @@ -# geniclient.py -# -# geni client -# -# implements the client-side of the GENI API. +## +# This module implements the client-side of the Geni API. Stubs are provided +# that convert the supplied parameters to the necessary format and send them +# via XMLRPC to a Geni Server. # # TODO: Investigate ways to combine this with existing PLC API? +## import xmlrpclib @@ -13,6 +13,7 @@ from credential import * from record import * from geniticket import * +## # ServerException, ExceptionUnmarshaller # # Used to convert server exception strings back to an exception. @@ -28,6 +29,7 @@ class ExceptionUnmarshaller(xmlrpclib.Unmarshaller): except xmlrpclib.Fault, e: raise ServerException(e.faultString) +## # GeniTransport # # A transport for XMLRPC that works on top of HTTPS @@ -49,19 +51,30 @@ class GeniTransport(xmlrpclib.Transport): else: return httplib.HTTPS(host, None, key_file=self.key_file, cert_file=self.cert_file) #**(x509 or {})) - def getparser(self): - unmarshaller = ExceptionUnmarshaller() - parser = xmlrpclib.ExpatParser(unmarshaller) + def getparser(self): + unmarshaller = ExceptionUnmarshaller() + parser = xmlrpclib.ExpatParser(unmarshaller) return parser, unmarshaller -# GeniClient: +## +# The GeniClient class provides stubs for executing Geni operations. A given +# client object connects to one server. To connect to multiple servers, create +# multiple GeniClient objects. # -# Class for performing GeniClient operations. +# The Geni protocol uses an HTTPS connection, and the client's side of the +# connection uses his private key. Generally, this private key must match the +# public key that is containing in the GID that the client is providing for +# those functions that take a GID. class GeniClient(): - # url = url of server - # key_file = private key file of client - # cert_file = x.509 cert of client + ## + # Create a new GeniClient object. + # + # @param url is the url of the server + # @param key_file = private key file of client + # @param cert_file = x.509 cert containing the client's public key. This + # could be a GID certificate, or any x.509 cert. + def __init__(self, url, key_file, cert_file): self.url = url self.key_file = key_file @@ -75,10 +88,31 @@ class GeniClient(): # Registry Interface # ------------------------------------------------------------------------- + ## + # Create a new GID. For MAs and SAs that are physically located on the + # registry, this allows a owner/operator/PI to create a new GID and have it + # signed by his respective authority. + # + # @param cred credential of caller + # @param name hrn for new GID + # @param uuid unique identifier for new GID + # @param pkey_string public-key string (TODO: why is this a string and not a keypair object?) + # + # @return a GID object + def create_gid(self, cred, name, uuid, pkey_string): gid_str = self.server.create_gid(cred.save_to_string(save_parents=True), name, uuid, pkey_string) return GID(string=gid_str) + ## + # Retrieve the GID for an object. This function looks up a record in the + # registry and returns the GID of the record if it exists. + # TODO: Is this function needed? It's a shortcut for Resolve() + # + # @param name hrn to look up + # + # @return a GID object + def get_gid(self, name): gid_str_list = self.server.get_gid(name) gid_list = [] @@ -86,22 +120,51 @@ class GeniClient(): gid_list.append(GID(string=str)) return gid_list - # get_self_credential + ## + # Get_self_credential a degenerate version of get_credential used by a + # client to get his initial credential when he doesn't have one. This is + # the same as get_credential(..., cred=None,...). # - # a degenerate version of get_credential used by a client to get his - # initial credential when he doesn't have one. The same as calling - # get_credential(..., cred=None,...) + # The registry ensures that the client is the principal that is named by + # (type, name) by comparing the public key in the record's GID to the + # private key used to encrypt the client-side of the HTTPS connection. Thus + # it is impossible for one principal to retrieve another principal's + # credential without having the appropriate private key. + # + # @param type type of object (user | slice | sa | ma | node + # @param name human readable name of object + # + # @return a credential object def get_self_credential(self, type, name): cred_str = self.server.get_self_credential(type, name) return Credential(string = cred_str) + ## + # Retrieve a credential for an object. + # + # If cred==None, then the behavior reverts to get_self_credential() + # + # @param cred credential object specifying rights of the caller + # @param type type of object (user | slice | sa | ma | node) + # @param name human readable name of object + # + # @return a credental object + def get_credential(self, cred, type, name): if cred == None: return self.get_self_credential(type, name) cred_str = self.server.get_credential(cred.save_to_string(save_parents=True), type, name) return Credential(string = cred_str) + ## + # List the records in an authority. The objectGID in the supplied credential + # should name the authority that will be listed. + # + # @param cred credential object specifying rights of the caller + # + # @return list of record objects + def list(self, cred): result_dict_list = self.server.list(cred.save_to_string(save_parents=True)) result_rec_list = [] @@ -109,14 +172,49 @@ class GeniClient(): result_rec_list.append(GeniRecord(dict=dict)) return result_rec_list + ## + # Register an object with the registry. In addition to being stored in the + # Geni database, the appropriate records will also be created in the + # PLC databases. + # + # The geni_info and/or pl_info fields must in the record must be filled + # out correctly depending on the type of record that is being registered. + # + # TODO: The geni_info member of the record should be parsed and the pl_info + # adjusted as necessary (add/remove users from a slice, etc) + # + # @param cred credential object specifying rights of the caller + # @return record to register + # + # @return GID object for the newly-registered record + def register(self, cred, record): gid_str = self.server.register(cred.save_to_string(save_parents=True), record.as_dict()) return GID(string = gid_str) + ## + # Remove an object from the registry. If the object represents a PLC object, + # then the PLC records will also be removed. + # + # @param cred credential object specifying rights of the caller + # @param record record to register. The only relevant + # fields of the record are 'name' and 'type', which are used to lookup + # the current copy of the record in the Geni database, to make sure + # that the appopriate record is removed. + def remove(self, cred, record): result = self.server.remove(cred.save_to_string(save_parents=True), record.as_dict()) return result + ## + # Resolve an object in the registry. A given HRN may have multiple records + # associated with it, and therefore multiple records may be returned. The + # caller should check the type fields of the records to find the one that + # he is interested in. + # + # @param cred credential object specifying rights of the caller + # @param name human readable name of object + def resolve(self, cred, name): result_dict_list = self.server.resolve(cred.save_to_string(save_parents=True), name) result_rec_list = [] @@ -124,6 +222,20 @@ class GeniClient(): result_rec_list.append(GeniRecord(dict=dict)) return result_rec_list + ## + # Update an object in the registry. Currently, this only updates the + # PLC information associated with the record. The Geni fields (name, type, + # GID) are fixed. + # + # The record is expected to have the pl_info field filled in with the data + # that should be updated. + # + # TODO: The geni_info member of the record should be parsed and the pl_info + # adjusted as necessary (add/remove users from a slice, etc) + # + # @param cred credential object specifying rights of the caller + # @param record a record object to be updated + def update(self, cred, record): result = self.server.update(cred.save_to_string(save_parents=True), record.as_dict()) return result @@ -132,31 +244,90 @@ class GeniClient(): # Slice Interface # ------------------------------------------------------------------------ + ## + # Start a slice. + # + # @param cred a credential identifying the caller (callerGID) and the slice + # (objectGID) + def start_slice(self, cred): result = self.server.start_slice(cred.save_to_string(save_parents=True)) return result + ## + # Stop a slice. + # + # @param cred a credential identifying the caller (callerGID) and the slice + # (objectGID) + def stop_slice(self, cred): result = self.server.stop_slice(cred.save_to_string(save_parents=True)) return result + ## + # Reset a slice. + # + # @param cred a credential identifying the caller (callerGID) and the slice + # (objectGID) + def reset_slice(self, cred): result = self.server.reset_slice(cred.save_to_string(save_parents=True)) return result + ## + # Delete a slice. + # + # @param cred a credential identifying the caller (callerGID) and the slice + # (objectGID) + def delete_slice(self, cred): result = self.server.delete_slice(cred.save_to_string(save_parents=True)) return result + ## + # List the slices on a component. + # + # @param cred credential object that authorizes the caller + # + # @return a list of slice names + def list_slices(self, cred): result = self.server.list_slices(cred.save_to_string(save_parents=True)) return result + ## + # Retrieve a ticket. This operation is currently implemented on the + # registry (see SFA, engineering decisions), and is not implemented on + # components. + # + # The ticket is filled in with information from the PLC database. This + # information includes resources, and attributes such as user keys and + # initscripts. + # + # @param cred credential object + # @param name name of the slice to retrieve a ticket for + # @param rspec resource specification dictionary + # + # @return a ticket object + def get_ticket(self, cred, name, rspec): ticket_str = self.server.get_ticket(cred.save_to_string(save_parents=True), name, rspec) ticket = Ticket(string=ticket_str) return ticket + ## + # Redeem a ticket. This operation is currently implemented on the + # component. + # + # The ticket is submitted to the node manager, and the slice is instantiated + # or updated as appropriate. + # + # TODO: This operation should return a sliver credential and indicate + # whether or not the component will accept only sliver credentials, or + # will accept both sliver and slice credentials. + # + # @param ticket a ticket object containing the ticket + def redeem_ticket(self, ticket): result = self.server.redeem_ticket(ticket.save_to_string(save_parents=True)) return result