rename list_slices to slices
[sfa.git] / geni / util / geniclient.py
1 ##
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.
5 #
6 # TODO: Investigate ways to combine this with existing PLC API?
7 ##
8
9 import xmlrpclib
10
11 from gid import *
12 from credential import *
13 from record import *
14 from geniticket import *
15
16 ##
17 # ServerException, ExceptionUnmarshaller
18 #
19 # Used to convert server exception strings back to an exception.
20 #    from usenet, Raghuram Devarakonda
21
22 class ServerException(Exception):
23     pass
24
25 class ExceptionUnmarshaller(xmlrpclib.Unmarshaller):
26     def close(self):\r
27         try:\r
28             return xmlrpclib.Unmarshaller.close(self)\r
29         except xmlrpclib.Fault, e:\r
30             raise ServerException(e.faultString)
31
32 ##
33 # GeniTransport
34 #
35 # A transport for XMLRPC that works on top of HTTPS
36
37 class GeniTransport(xmlrpclib.Transport):
38     key_file = None
39     cert_file = None
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
43         import httplib\r
44         host, extra_headers, x509 = self.get_host_info(host)\r
45         try:\r
46             HTTPS = httplib.HTTPS()\r
47         except AttributeError:\r
48             raise NotImplementedError(\r
49                 "your version of httplib doesn't support HTTPS"\r
50                 )\r
51         else:\r
52             return httplib.HTTPS(host, None, key_file=self.key_file, cert_file=self.cert_file) #**(x509 or {}))\r
53 \r
54     def getparser(self):\r
55         unmarshaller = ExceptionUnmarshaller()\r
56         parser = xmlrpclib.ExpatParser(unmarshaller)\r
57         return parser, unmarshaller\r
58 \r
59 ##\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
63 #\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
68 \r
69 class GeniClient():
70     ##
71     # Create a new GeniClient object.
72     #
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.
77
78     def __init__(self, url, key_file, cert_file):
79        self.url = url
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)
86
87     # -------------------------------------------------------------------------
88     # Registry Interface
89     # -------------------------------------------------------------------------
90
91     ##
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.
95     #
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?)
100     #
101     # @return a GID object
102
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)
106
107     ##
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()
111     #
112     # @param name hrn to look up
113     #
114     # @return a GID object
115
116     def get_gid(self, name):
117        gid_str_list = self.server.get_gid(name)
118        gid_list = []
119        for str in gid_str_list:
120            gid_list.append(GID(string=str))
121        return gid_list
122
123     ##
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,...).
127     #
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.
133     #
134     # @param type type of object (user | slice | sa | ma | node
135     # @param name human readable name of object
136     #
137     # @return a credential object
138
139     def get_self_credential(self, type, name):
140         cred_str = self.server.get_self_credential(type, name)
141         return Credential(string = cred_str)
142
143     ##
144     # Retrieve a credential for an object.
145     #
146     # If cred==None, then the behavior reverts to get_self_credential()
147     #
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
151     #
152     # @return a credental object
153
154     def get_credential(self, cred, type, name):
155         if cred:
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)
159
160     ##
161     # List the records in an authority. The objectGID in the supplied credential
162     # should name the authority that will be listed.
163     #
164     # @param cred credential object specifying rights of the caller
165     #
166     # @return list of record objects
167
168     def list(self, cred, auth_hrn):
169         result_dict_list = self.server.list(cred.save_to_string(save_parents=True), auth_hrn)
170         result_rec_list = []
171         for dict in result_dict_list:
172              result_rec_list.append(GeniRecord(dict=dict))
173         return result_rec_list
174
175     ##
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
178     # PLC databases.
179     #
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.
182     #
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)
185     #
186     # @param cred credential object specifying rights of the caller
187     # @return record to register
188     #
189     # @return GID object for the newly-registered record
190
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)
194
195     ##
196     # Remove an object from the registry. If the object represents a PLC object,
197     # then the PLC records will also be removed.
198     #
199     # @param cred credential object specifying rights of the caller
200     # @param type
201     # @param hrn
202
203     def remove(self, cred, type, hrn):
204         result = self.server.remove(cred.save_to_string(save_parents=True), type, hrn)
205         return result
206
207     ##
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.
212     #
213     # @param cred credential object specifying rights of the caller
214     # @param name human readable name of object
215
216     def resolve(self, cred, name):
217         result_dict_list = self.server.resolve(cred.save_to_string(save_parents=True), name)
218         result_rec_list = []
219         for dict in result_dict_list:
220              result_rec_list.append(GeniRecord(dict=dict))
221         return result_rec_list
222
223     ##
224     # Update an object in the registry. Currently, this only updates the
225     # PLC information associated with the record. The Geni fields (name, type,
226     # GID) are fixed.
227     #
228     # The record is expected to have the pl_info field filled in with the data
229     # that should be updated.
230     #
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)
233     #
234     # @param cred credential object specifying rights of the caller
235     # @param record a record object to be updated
236
237     def update(self, cred, record):
238         result = self.server.update(cred.save_to_string(save_parents=True), record.as_dict())
239         return result
240
241
242     #-------------------------------------------------------------------------
243     # Aggregate Interface
244     #-------------------------------------------------------------------------
245     
246     ## list resources
247     #
248     # @param cred a credential
249     # @param hrn slice hrn
250
251     def resources(self, cred, hrn=None):
252         result = self.server.resources(cred.save_to_string(save_parents=True), hrn)
253         return result
254
255     ## get policy
256     #
257     # @param cred a credential
258
259     def get_policy(self, cred):
260         result = self.server.get_policy(cred.save_to_string(save_parents=True))
261         return result
262
263     ## create slice
264     #
265     # @param cred a credential
266     # @param rspec resource specification defining how to instantiate the slice
267     
268     def create_slice(self, cred, hrn, rspec):
269         result = self.server.create_slice(cred.save_to_string(save_parents=True), hrn, rspec)
270         return result
271
272
273     ## delete slice
274     #
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)
279         return result    
280
281     # ------------------------------------------------------------------------
282     # Slice Interface
283     # ------------------------------------------------------------------------
284
285     ##
286     # Start a slice.
287     #
288     # @param cred a credential identifying the caller (callerGID) and the slice
289     #     (objectGID)
290
291     def start_slice(self, cred):
292         result = self.server.start_slice(cred.save_to_string(save_parents=True))
293         return result
294
295     ##
296     # Stop a slice.
297     #
298     # @param cred a credential identifying the caller (callerGID) and the slice
299     #     (objectGID)
300
301     def stop_slice(self, cred):
302         result = self.server.stop_slice(cred.save_to_string(save_parents=True))
303         return result
304
305     ##
306     # Reset a slice.
307     #
308     # @param cred a credential identifying the caller (callerGID) and the slice
309     #     (objectGID)
310
311     def reset_slice(self, cred):
312         result = self.server.reset_slice(cred.save_to_string(save_parents=True))
313         return result
314
315     ##
316     # Delete a slice.
317     #
318     # @param cred a credential identifying the caller (callerGID) and the slice
319     #     (objectGID)
320
321     def delete_slice(self, cred, hrn):
322         result = self.server.delete_slice(cred.save_to_string(save_parents=True), hrn)
323         return result
324
325     ##
326     # List the slices on a component.
327     #
328     # @param cred credential object that authorizes the caller
329     #
330     # @return a list of slice names
331
332     def slices(self, cred):
333         result = self.server.slices(cred.save_to_string(save_parents=True))
334         return result
335
336     ##
337     # Retrieve a ticket. This operation is currently implemented on the
338     # registry (see SFA, engineering decisions), and is not implemented on
339     # components.
340     #
341     # The ticket is filled in with information from the PLC database. This
342     # information includes resources, and attributes such as user keys and
343     # initscripts.
344     #
345     # @param cred credential object
346     # @param name name of the slice to retrieve a ticket for
347     # @param rspec resource specification dictionary
348     #
349     # @return a ticket object
350
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)
354         return ticket
355
356     ##
357     # Redeem a ticket. This operation is currently implemented on the
358     # component.
359     #
360     # The ticket is submitted to the node manager, and the slice is instantiated
361     # or updated as appropriate.
362     #
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.
366     #
367     # @param ticket a ticket object containing the ticket
368
369     def redeem_ticket(self, ticket):
370         result = self.server.redeem_ticket(ticket.save_to_string(save_parents=True))
371         return result
372
373