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