2 # This module implements the client-side of the Geni API. Stubs are provided
3 # that convert the supplied parameters to the necessary format and send them
4 # via XMLRPC to a Geni Server.
6 # TODO: Investigate ways to combine this with existing PLC API?
12 from credential import *
14 from geniticket import *
17 # ServerException, ExceptionUnmarshaller
19 # Used to convert server exception strings back to an exception.
20 # from usenet, Raghuram Devarakonda
22 class ServerException(Exception):
25 class ExceptionUnmarshaller(xmlrpclib.Unmarshaller):
28 return xmlrpclib.Unmarshaller.close(self)
\r
29 except xmlrpclib.Fault, e:
\r
30 raise ServerException(e.faultString)
35 # A transport for XMLRPC that works on top of HTTPS
37 class GeniTransport(xmlrpclib.Transport):
40 def make_connection(self, host):
\r
41 # create a HTTPS connection object from a host descriptor
\r
42 # host may be a string, or a (host, x509-dict) tuple
\r
44 host, extra_headers, x509 = self.get_host_info(host)
\r
46 HTTPS = httplib.HTTPS()
\r
47 except AttributeError:
\r
48 raise NotImplementedError(
\r
49 "your version of httplib doesn't support HTTPS"
\r
52 return httplib.HTTPS(host, None, key_file=self.key_file, cert_file=self.cert_file) #**(x509 or {}))
\r
54 def getparser(self):
\r
55 unmarshaller = ExceptionUnmarshaller()
\r
56 parser = xmlrpclib.ExpatParser(unmarshaller)
\r
57 return parser, unmarshaller
\r
60 # The GeniClient class provides stubs for executing Geni operations. A given
\r
61 # client object connects to one server. To connect to multiple servers, create
\r
62 # multiple GeniClient objects.
\r
64 # The Geni protocol uses an HTTPS connection, and the client's side of the
\r
65 # connection uses his private key. Generally, this private key must match the
\r
66 # public key that is containing in the GID that the client is providing for
\r
67 # those functions that take a GID.
\r
71 # Create a new GeniClient object.
73 # @param url is the url of the server
74 # @param key_file = private key file of client
75 # @param cert_file = x.509 cert containing the client's public key. This
76 # could be a GID certificate, or any x.509 cert.
78 def __init__(self, url, key_file, cert_file):
80 self.key_file = key_file
81 self.cert_file = cert_file
82 self.transport = GeniTransport()
83 self.transport.key_file = self.key_file
84 self.transport.cert_file = self.cert_file
85 self.server = xmlrpclib.ServerProxy(self.url, self.transport, allow_none=True)
87 # -------------------------------------------------------------------------
89 # -------------------------------------------------------------------------
92 # Create a new GID. For MAs and SAs that are physically located on the
93 # registry, this allows a owner/operator/PI to create a new GID and have it
94 # signed by his respective authority.
96 # @param cred credential of caller
97 # @param name hrn for new GID
98 # @param uuid unique identifier for new GID
99 # @param pkey_string public-key string (TODO: why is this a string and not a keypair object?)
101 # @return a GID object
103 def create_gid(self, cred, name, uuid, pkey_string):
104 gid_str = self.server.create_gid(cred.save_to_string(save_parents=True), name, uuid, pkey_string)
105 return GID(string=gid_str)
108 # Retrieve the GID for an object. This function looks up a record in the
109 # registry and returns the GID of the record if it exists.
110 # TODO: Is this function needed? It's a shortcut for Resolve()
112 # @param name hrn to look up
114 # @return a GID object
116 def get_gid(self, name):
117 gid_str_list = self.server.get_gid(name)
119 for str in gid_str_list:
120 gid_list.append(GID(string=str))
124 # Get_self_credential a degenerate version of get_credential used by a
125 # client to get his initial credential when he doesn't have one. This is
126 # the same as get_credential(..., cred=None,...).
128 # The registry ensures that the client is the principal that is named by
129 # (type, name) by comparing the public key in the record's GID to the
130 # private key used to encrypt the client-side of the HTTPS connection. Thus
131 # it is impossible for one principal to retrieve another principal's
132 # credential without having the appropriate private key.
134 # @param type type of object (user | slice | sa | ma | node
135 # @param name human readable name of object
137 # @return a credential object
139 def get_self_credential(self, type, name):
140 cred_str = self.server.get_self_credential(type, name)
141 return Credential(string = cred_str)
144 # Retrieve a credential for an object.
146 # If cred==None, then the behavior reverts to get_self_credential()
148 # @param cred credential object specifying rights of the caller
149 # @param type type of object (user | slice | sa | ma | node)
150 # @param name human readable name of object
152 # @return a credental object
154 def get_credential(self, cred, type, name):
156 cred = cred.save_to_string(save_parents=True)
157 cred_str = self.server.get_credential(cred, type, name)
158 return Credential(string = cred_str)
161 # List the records in an authority. The objectGID in the supplied credential
162 # should name the authority that will be listed.
164 # @param cred credential object specifying rights of the caller
166 # @return list of record objects
168 def list(self, cred, auth_hrn):
169 result_dict_list = self.server.list(cred.save_to_string(save_parents=True), auth_hrn)
171 for dict in result_dict_list:
172 result_rec_list.append(GeniRecord(dict=dict))
173 return result_rec_list
176 # Register an object with the registry. In addition to being stored in the
177 # Geni database, the appropriate records will also be created in the
180 # The geni_info and/or pl_info fields must in the record must be filled
181 # out correctly depending on the type of record that is being registered.
183 # TODO: The geni_info member of the record should be parsed and the pl_info
184 # adjusted as necessary (add/remove users from a slice, etc)
186 # @param cred credential object specifying rights of the caller
187 # @return record to register
189 # @return GID object for the newly-registered record
191 def register(self, cred, record):
192 gid_str = self.server.register(cred.save_to_string(save_parents=True), record.as_dict())
193 return GID(string = gid_str)
196 # Remove an object from the registry. If the object represents a PLC object,
197 # then the PLC records will also be removed.
199 # @param cred credential object specifying rights of the caller
203 def remove(self, cred, type, hrn):
204 result = self.server.remove(cred.save_to_string(save_parents=True), type, hrn)
208 # Resolve an object in the registry. A given HRN may have multiple records
209 # associated with it, and therefore multiple records may be returned. The
210 # caller should check the type fields of the records to find the one that
211 # he is interested in.
213 # @param cred credential object specifying rights of the caller
214 # @param name human readable name of object
216 def resolve(self, cred, name):
217 result_dict_list = self.server.resolve(cred.save_to_string(save_parents=True), name)
219 for dict in result_dict_list:
220 result_rec_list.append(GeniRecord(dict=dict))
221 return result_rec_list
224 # Update an object in the registry. Currently, this only updates the
225 # PLC information associated with the record. The Geni fields (name, type,
228 # The record is expected to have the pl_info field filled in with the data
229 # that should be updated.
231 # TODO: The geni_info member of the record should be parsed and the pl_info
232 # adjusted as necessary (add/remove users from a slice, etc)
234 # @param cred credential object specifying rights of the caller
235 # @param record a record object to be updated
237 def update(self, cred, record):
238 result = self.server.update(cred.save_to_string(save_parents=True), record.as_dict())
242 #-------------------------------------------------------------------------
243 # Aggregate Interface
244 #-------------------------------------------------------------------------
248 # @param cred a credential
249 # @param hrn slice hrn
251 def resources(self, cred, hrn=None):
252 result = self.server.resources(cred.save_to_string(save_parents=True), hrn)
257 # @param cred a credential
259 def get_policy(self, cred):
260 result = self.server.get_policy(cred.save_to_string(save_parents=True))
265 # @param cred a credential
266 # @param rspec resource specification defining how to instantiate the slice
268 def create_slice(self, cred, hrn, rspec):
269 result = self.server.create_slice(cred.save_to_string(save_parents=True), hrn, rspec)
275 # @param cred a credential
276 # @param hrn slice to delete
277 def delete_slice(self, cred, hrn):
278 result = self.server.delete_slice(cred.save_to_string(save_parents=True), hrn)
281 # ------------------------------------------------------------------------
283 # ------------------------------------------------------------------------
288 # @param cred a credential identifying the caller (callerGID) and the slice
291 def start_slice(self, cred):
292 result = self.server.start_slice(cred.save_to_string(save_parents=True))
298 # @param cred a credential identifying the caller (callerGID) and the slice
301 def stop_slice(self, cred):
302 result = self.server.stop_slice(cred.save_to_string(save_parents=True))
308 # @param cred a credential identifying the caller (callerGID) and the slice
311 def reset_slice(self, cred):
312 result = self.server.reset_slice(cred.save_to_string(save_parents=True))
318 # @param cred a credential identifying the caller (callerGID) and the slice
321 def delete_slice(self, cred, hrn):
322 result = self.server.delete_slice(cred.save_to_string(save_parents=True), hrn)
326 # List the slices on a component.
328 # @param cred credential object that authorizes the caller
330 # @return a list of slice names
332 def slices(self, cred):
333 result = self.server.slices(cred.save_to_string(save_parents=True))
337 # Retrieve a ticket. This operation is currently implemented on the
338 # registry (see SFA, engineering decisions), and is not implemented on
341 # The ticket is filled in with information from the PLC database. This
342 # information includes resources, and attributes such as user keys and
345 # @param cred credential object
346 # @param name name of the slice to retrieve a ticket for
347 # @param rspec resource specification dictionary
349 # @return a ticket object
351 def get_ticket(self, cred, name, rspec):
352 ticket_str = self.server.get_ticket(cred.save_to_string(save_parents=True), name, rspec)
353 ticket = Ticket(string=ticket_str)
357 # Redeem a ticket. This operation is currently implemented on the
360 # The ticket is submitted to the node manager, and the slice is instantiated
361 # or updated as appropriate.
363 # TODO: This operation should return a sliver credential and indicate
364 # whether or not the component will accept only sliver credentials, or
365 # will accept both sliver and slice credentials.
367 # @param ticket a ticket object containing the ticket
369 def redeem_ticket(self, ticket):
370 result = self.server.redeem_ticket(ticket.save_to_string(save_parents=True))