sfa-0.9-17 tag
[sfa.git] / sfa / util / client.py
1 ##
2 # This module implements the client-side of the SFA API. Stubs are provided
3 # that convert the supplied parameters to the necessary format and send them
4 # via XMLRPC to an SFA Server.
5 #
6 # TODO: Investigate ways to combine this with existing PLC API?
7 ##
8
9 ### $Id: client.py 16477 2010-01-05 16:31:37Z thierry $
10 ### $URL: http://svn.planet-lab.org/svn/sfa/trunk/sfa/util/client.py $
11
12 from sfa.trust.certificate import *
13 from sfa.trust.gid import *
14 from sfa.trust.credential import *
15 from sfa.util.record import *
16 from sfa.util.sfaticket import SfaTicket
17
18 ##
19 # The GeniClient class provides stubs for executing SFA operations. A given
20 # client object connects to one server. To connect to multiple servers, create
21 # multiple GeniClient objects.
22 #
23 # The SFA protocol uses an HTTPS connection, and the client's side of the
24 # connection uses his private key. Generally, this private key must match the
25 # public key that is containing in the GID that the client is providing for
26 # those functions that take a GID.
27
28 class GeniClient:
29     ##
30     # Create a new GeniClient object.
31     #
32     # @param url is the url of the server
33     # @param key_file = private key file of client
34     # @param cert_file = x.509 cert containing the client's public key. This
35     #      could be a GID certificate, or any x.509 cert.
36     # @param protocol The ORPC protocol to use. Can be "soap" or "xmlrpc"
37
38     def __init__(self, url, key_file, cert_file, protocol="xmlrpc"):
39         self.url = url
40         self.key_file = key_file
41         self.cert_file = cert_file
42         self.key = Keypair(filename = self.key_file)
43     
44
45         if (protocol=="xmlrpc"):
46             import xmlrpcprotocol  
47             self.server = xmlrpcprotocol.get_server(self.url, self.key_file, self.cert_file)
48         elif (protocol=="soap"):
49             import soapprotocol
50             self.server = soapprotocol.get_server(self.url, self.key_file, self.cert_file)
51         else:
52             raise Exception("Attempted use of undefined protocol %s"%protocol)
53
54
55     # -------------------------------------------------------------------------
56     # Registry Interface
57     # -------------------------------------------------------------------------
58
59     ##
60     # Create a new GID. For MAs and SAs that are physically located on the
61     # registry, this allows a owner/operator/PI to create a new GID and have it
62     # signed by his respective authority.
63     #
64     # @param cred credential of caller
65     # @param name hrn for new GID
66     # @param uuid unique identifier for new GID
67     # @param pkey_string public-key string (TODO: why is this a string and not a keypair object?)
68     #
69     # @return a GID object
70
71     def create_gid(self, cred, name, uuid, pkey_string):
72         gid_str = self.server.create_gid(cred.save_to_string(save_parents=True), name, uuid, pkey_string)
73         return GID(string=gid_str)
74
75     ##
76     # Retrieve the GID for an object. This function looks up a record in the
77     # registry and returns the GID of the record if it exists.
78     # TODO: Is this function needed? It's a shortcut for Resolve()
79     #
80     # @param name hrn to look up
81     #
82     # @return a GID object
83
84     #def get_gid(self, name):
85     #   gid_str_list = self.server.get_gid(name)
86     #   gid_list = []
87     #   for str in gid_str_list:
88     #       gid_list.append(GID(string=str))
89     #  return gid_list
90
91
92     def get_gid(self, cert, hrn, type, request_hash):
93         cert_string = cert.save_to_string(save_parents=True)
94         gid_str = self.server.get_gid(cert_string, hrn, type, request_hash)
95         return GID(string=gid_str)
96     ##
97     # Get_self_credential a degenerate version of get_credential used by a
98     # client to get his initial credential when he doesn't have one. This is
99     # the same as get_credential(..., cred=None,...).
100     #
101     # The registry ensures that the client is the principal that is named by
102     # (type, name) by comparing the public key in the record's GID to the
103     # private key used to encrypt the client-side of the HTTPS connection. Thus
104     # it is impossible for one principal to retrieve another principal's
105     # credential without having the appropriate private key.
106     #
107     # @param type type of object (user | slice | sa | ma | node
108     # @param name human readable name of object
109     #
110     # @return a credential object
111
112     def get_self_credential(self, type, name):
113         cred_str = self.server.get_self_credential(type, name)
114         return Credential(string = cred_str)
115
116     ##
117     # Retrieve a credential for an object.
118     #
119     # If cred==None, then the behavior reverts to get_self_credential()
120     #
121     # @param cred credential object specifying rights of the caller
122     # @param type type of object (user | slice | sa | ma | node)
123     # @param name human readable name of object
124     #
125     # @return a credental object
126
127     def get_credential(self, cred, type, name):
128         if cred:
129             cred = cred.save_to_string(save_parents=True) 
130         cred_str = self.server.get_credential(cred, type, name)
131         return Credential(string = cred_str)
132
133     ##
134     # List the records in an authority. The objectGID in the supplied credential
135     # should name the authority that will be listed.
136     #
137     # @param cred credential object specifying rights of the caller
138     #
139     # @return list of record objects
140
141     def list(self, cred, auth_hrn, caller_cred=None):
142         result_dict_list = self.server.list(cred.save_to_string(save_parents=True), auth_hrn, caller_cred)
143         result_rec_list = []
144         for dict in result_dict_list:
145              result_rec_list.append(SfaRecord(dict=dict))
146         return result_rec_list
147
148     ##
149     # Register an object with the registry. In addition to being stored in the
150     # SFA database, the appropriate records will also be created in the
151     # PLC databases.
152     #
153     #
154     #
155     # @param cred credential object specifying rights of the caller
156     # @param record to register
157     #
158     # @return GID object for the newly-registered record
159
160     def register(self, cred, record, caller_cred=None):
161         gid_str = self.server.register(cred.save_to_string(save_parents=True), record.as_dict(), caller_cred)
162         return GID(string = gid_str)
163
164     
165     ##
166     # Register a peer object with the registry. 
167     #
168     #
169     # @param cred credential object specifying rights of the caller
170     # @param record to register
171     #
172     # @return GID object for the newly-registered record
173
174     def register_peer_object(self, cred, record, caller_cred=None):
175         return self.server.register_peer_object(cred.save_to_string(save_parents=True), record, caller_cred)
176
177     ##
178     # Remove an object from the registry. If the object represents a PLC object,
179     # then the PLC records will also be removed.
180     #
181     # @param cred credential object specifying rights of the caller
182     # @param type
183     # @param hrn
184
185     def remove(self, cred, type, hrn, caller_cred=None):
186         return self.server.remove(cred.save_to_string(save_parents=True), type, hrn, caller_cred)
187
188     ##
189     # Remove a peer object from the registry. If the object represents a PLC object,
190     # then the PLC records will also be removed.
191     #
192     # @param cred credential object specifying rights of the caller
193     # @param type
194     # @param hrn
195
196     def remove_peer_object(self, cred, record, caller_cred=None):
197         result = self.server.remove_peer_object(cred.save_to_string(save_parents=True), record, caller_cred)
198         return result
199
200     ##
201     # Resolve an object in the registry. A given HRN may have multiple records
202     # associated with it, and therefore multiple records may be returned. The
203     # caller should check the type fields of the records to find the one that
204     # he is interested in.
205     #
206     # @param cred credential object specifying rights of the caller
207     # @param name human readable name of object
208
209     def resolve(self, cred, name, caller_cred=None):
210         result_dict_list = self.server.resolve(cred.save_to_string(save_parents=True), name, caller_cred)
211         result_rec_list = []
212         for dict in result_dict_list:
213             if dict['type'] in ['authority']:
214                 result_rec_list.append(AuthorityRecord(dict=dict))
215             elif dict['type'] in ['node']:
216                 result_rec_list.append(NodeRecord(dict=dict))
217             elif dict['type'] in ['slice']:
218                 result_rec_list.append(SliceRecord(dict=dict))
219             elif dict['type'] in ['user']:
220                 result_rec_list.append(UserRecord(dict=dict))
221             else:
222                 result_rec_list.append(SfaRecord(dict=dict))
223         return result_rec_list
224
225     ##
226     # Update an object in the registry. Currently, this only updates the
227     # PLC information associated with the record. The SFA fields (name, type,
228     # GID) are fixed.
229     #
230     #
231     #
232     # @param cred credential object specifying rights of the caller
233     # @param record a record object to be updated
234
235     def update(self, cred, record, caller_cred=None):
236         result = self.server.update(cred.save_to_string(save_parents=True), record.as_dict(), caller_cred)
237         return result
238
239
240     #-------------------------------------------------------------------------
241     # Aggregate Interface
242     #-------------------------------------------------------------------------
243     
244     ## list resources
245     #
246     # @param cred a credential
247     # @param hrn slice hrn
248
249     def get_resources(self, cred, hrn=None, caller_cred=None):
250         result = self.server.get_resources(cred.save_to_string(save_parents=True), hrn, caller_cred)
251         return result
252
253     def get_aggregates(self, cred, hrn=None):
254         result = self.server.get_aggregates(cred.save_to_string(save_parents=True), hrn)
255         return result
256
257     def get_registries(self, cred, hrn=None):
258         result = self.server.get_registries(cred.save_to_string(save_parents=True), hrn)
259         return result
260
261     ## get policy
262     #
263     # @param cred a credential
264
265     def get_policy(self, cred):
266         result = self.server.get_policy(cred.save_to_string(save_parents=True))
267         return result
268
269     ## create slice
270     #
271     # @param cred a credential
272     # @param rspec resource specification defining how to instantiate the slice
273     
274     def create_slice(self, cred, hrn, rspec, caller_cred=None):
275         result = self.server.create_slice(cred.save_to_string(save_parents=True), hrn, rspec, caller_cred)
276         return result
277
278
279     ## delete slice
280     #
281     # @param cred a credential
282     # @param hrn slice to delete
283     def delete_slice(self, cred, hrn, caller_cred=None):
284         result = self.server.delete_slice(cred.save_to_string(save_parents=True), hrn, caller_cred)
285         return result    
286
287     # ------------------------------------------------------------------------
288     # Slice Interface
289     # ------------------------------------------------------------------------
290
291     ##
292     # Start a slice.
293     #
294     # @param cred a credential identifying the caller (callerGID) and the slice
295     #     (objectGID)
296
297     def start_slice(self, cred, hrn):
298         result = self.server.start_slice(cred.save_to_string(save_parents=True), hrn)
299         return result
300
301     ##
302     # Stop a slice.
303     #
304     # @param cred a credential identifying the caller (callerGID) and the slice
305     #     (objectGID)
306
307     def stop_slice(self, cred, hrn):
308         result = self.server.stop_slice(cred.save_to_string(save_parents=True), hrn)
309         return result
310
311     ##
312     # Reset a slice.
313     #
314     # @param cred a credential identifying the caller (callerGID) and the slice
315     #     (objectGID)
316
317     def reset_slice(self, cred, hrn):
318         result = self.server.reset_slice(cred.save_to_string(save_parents=True), hrn)
319         return result
320
321     ##
322     # Delete a slice.
323     #
324     # @param cred a credential identifying the caller (callerGID) and the slice
325     #     (objectGID)
326
327     def delete_slice(self, cred, hrn, caller_cred=None):
328         result = self.server.delete_slice(cred.save_to_string(save_parents=True), hrn, caller_cred)
329         return result
330
331     ##
332     # List the slices on a component.
333     #
334     # @param cred credential object that authorizes the caller
335     #
336     # @return a list of slice names
337
338     def get_slices(self, cred):
339         result = self.server.get_slices(cred.save_to_string(save_parents=True))
340         return result
341
342     ##
343     # Retrieve a ticket. This operation is currently implemented on the
344     # registry (see SFA, engineering decisions), and is not implemented on
345     # components.
346     #
347     # The ticket is filled in with information from the PLC database. This
348     # information includes resources, and attributes such as user keys and
349     # initscripts.
350     #
351     # @param cred credential object
352     # @param name name of the slice to retrieve a ticket for
353     # @param rspec resource specification dictionary
354     #
355     # @return a ticket object
356
357     def get_ticket(self, cred, name, rspec):
358         ticket_str = self.server.get_ticket(cred.save_to_string(save_parents=True), name, rspec)
359         ticket = SfaTicket(string=ticket_str)
360         return ticket
361
362     ##
363     # Redeem a ticket. This operation is currently implemented on the
364     # component.
365     #
366     # The ticket is submitted to the node manager, and the slice is instantiated
367     # or updated as appropriate.
368     #
369     # TODO: This operation should return a sliver credential and indicate
370     # whether or not the component will accept only sliver credentials, or
371     # will accept both sliver and slice credentials.
372     #
373     # @param ticket a ticket object containing the ticket
374
375     def redeem_ticket(self, ticket):
376         result = self.server.redeem_ticket(ticket.save_to_string(save_parents=True))
377         return result
378
379
380     def remove_remote_object(self, cred, hrn, record):
381         result = self.server.remove_remote_object(cred.save_to_string(save_parents=True), hrn, record)
382         return result