Automatically drop spaces that I was previously dropping manually.
[sfa.git] / component / component.py
1 ##
2 # Sfa Component Wrapper
3 #
4 # This wrapper implements the SFA Slice and Mgmt Interfaces on a node.
5 #
6 ##
7
8 import tempfile
9 import os
10 import sys
11 from xmlrpclib import ServerProxy
12
13 from sfa.trust.certificate import Certificate, Keypair
14 from sfa.trust.gid import *
15 from sfa.trust.trustedroot import *
16
17 from sfa.util.faults import *
18 from sfa.util.misc import *
19 from sfa.util.record import *
20 from sfa.util.geniticket import *
21 from sfa.util.geniserver import *
22
23 ##
24 # ComponentManager is a GeniServer that serves slice and
25 # management operations at a node.
26
27 class ComponentManager(GeniServer):
28
29     ##
30     # Create a new ComponentManager object.
31     #
32     # @param ip the ip address to listen on
33     # @param port the port to listen on
34     # @param key_file private key filename of registry
35     # @param cert_file certificate filename containing public key (could be a GID file)
36
37     def __init__(self, ip, port, key_file, cert_file):
38         GeniServer.__init__(self, ip, port, key_file, cert_file)
39         self.nodemanager = ServerProxy('http://127.0.0.1:812/')
40
41     ##
42     # Register the server RPCs for the component
43
44     def register_functions(self):
45         GeniServer.register_functions(self)
46         self.server.register_function(self.stop_slice)
47         self.server.register_function(self.start_slice)
48         self.server.register_function(self.reset_slice)
49         self.server.register_function(self.delete_slice)
50         self.server.register_function(self.list_slices)
51         self.server.register_function(self.redeem_ticket)
52         self.server.register_function(self.reboot)
53
54     def sliver_exists(self, slicename):
55         dict = self.nodemanager.GetXIDs()
56         if slicename in dict.keys():
57             return True
58         else:
59             return False
60
61     # ------------------------------------------------------------------------
62     # Slice Interface
63
64     ##
65     # Stop a slice.
66     #
67     # @param cred a credential identifying the caller (callerGID) and the slice
68     #     (objectGID)
69
70     def stop_slice(self, cred_str):
71         self.decode_authentication(cred_str, "stopslice")
72         slicename = hrn_to_pl_slicename(self.object_gid.get_hrn())
73         print "stopslice:", slicename
74         self.nodemanager.Stop(slicename)
75
76     ##
77     # Start a slice.
78     #
79     # @param cred a credential identifying the caller (callerGID) and the slice
80     #     (objectGID)
81
82     def start_slice(self, cred_str):
83         self.decode_authentication(cred_str, "startslice")
84         slicename = hrn_to_pl_slicename(self.object_gid.get_hrn())
85         print "startslice:", slicename
86         self.nodemanager.Start(slicename)
87
88     ##
89     # Reset a slice.
90     #
91     # @param cred a credential identifying the caller (callerGID) and the slice
92     #     (objectGID)
93
94     def reset_slice(self, cred_str):
95         self.decode_authentication(cred_str, "resetslice")
96         slicename = hrn_to_pl_slicename(self.object_gid.get_hrn())
97         print "resetslice:", slicename
98
99         # find the existing record for the slice
100         if not self.sliver_exists(slicename):
101             raise SliverDoesNotExist(slicename)
102
103         self.nodemanager.ReCreate(slicename)
104
105     ##
106     # Delete a slice.
107     #
108     # @param cred a credential identifying the caller (callerGID) and the slice
109     #     (objectGID)
110
111     def delete_slice(self, cred_str):
112         self.decode_authentication(cred_str, "deleteslice")
113         slicename = hrn_to_pl_slicename(self.object_gid.get_hrn())
114         print "deleteslice:", slicename
115         self.nodemanager.Destroy(slicename)
116
117     ##
118     # Examine the ticket that was provided by the caller, check that it is
119     # signed and verified correctly. Throw an exception if something is
120     # wrong with the ticket.
121     #
122     # This is similar to geniserver.decode_authentication
123     #
124     # @param ticket_string the string representation of the ticket
125
126     def decode_ticket(self, ticket_string):
127         self.client_ticket = Ticket(string = ticket_string)
128         self.client_gid = self.client_ticket.get_gid_caller()
129         self.object_gid = self.client_ticket.get_gid_object()
130
131         # make sure the client_gid is not blank
132         if not self.client_gid:
133             raise MissingCallerGID(self.client_ticket.get_subject())
134
135         # make sure the client_gid matches the certificate that the client is using
136         peer_cert = self.server.peer_cert
137         if not peer_cert.is_pubkey(self.client_gid.get_pubkey()):
138             raise ConnectionKeyGIDMismatch(self.client_gid.get_subject())
139
140         if self.trusted_cert_list:
141             self.client_ticket.verify_chain(self.trusted_cert_list)
142             if self.client_gid:
143                 self.client_gid.verify_chain(self.trusted_cert_list)
144             if self.object_gid:
145                 self.object_gid.verify_chain(self.trusted_cert_list)
146
147     def geni_ticket_to_plc_ticket(self, ticket):
148         ticket_attrs = ticket.get_attributes()
149         ticket_rspec = ticket.get_rspec()
150
151         data = {}
152         rec = {}
153         attr_list = []
154
155         # sort out the initscript... The NM expects to receive an initscript name
156         # and a dictionary of initscripts. NM ends up discarding the initscript
157         # name and sticking the contents in the slice record. (technically, this
158         # is what we started with, but we have to provide the data in the format
159         # that the NM expects)
160         if ticket_attrs.get("initscript", None):
161             initscript_name = ticket_attrs.get("name") + "_initscript"
162             initscript_body = ticket_attrs.get("initscript")
163             data["initscripts"] = {"name": initscript_name, "script": initscript_body}
164             attr_dict["initscript"] = initscript_name
165         else:
166             data["initscripts"] = {}
167
168         # copy the rspec attributes from the geniticket into the plticket
169         # attributes. The NM will later copy them back out and put them into
170         # the rspec field of the slice record
171         for itemname in ticket_rspec.keys():
172             attr = {"name": itemname, "value": ticket_rspec[itemname]}
173             attr_list.append(attr)
174
175         # NM expects to receive a list of key dictionaries containing the
176         # keys.
177         keys = []
178         for key in ticket_attrs.get("keys", []):
179             keys.append({"key": key})
180         rec["keys"] = keys
181
182         rec["name"] = ticket_attrs.get("name")
183
184         rec["attributes"] = attr_list
185         rec["instantiation"] = ticket_attrs["instantiation"]
186         rec["slice_id"] = ticket_attrs["slice_id"]
187
188         # XXX - this shouldn't be hardcoded; use the actual slice name
189         rec["delegations"] = "pl_genicw"
190
191         data["timestamp"] = ticket_attrs.get("timestamp")
192         data["slivers"] = [rec]
193
194         return data
195
196     ##
197     # Redeem a ticket.
198     #
199     # The ticket is submitted to the node manager, and the slice is instantiated
200     # or updated as appropriate.
201     #
202     # TODO: This operation should return a sliver credential and indicate
203     # whether or not the component will accept only sliver credentials, or
204     # will accept both sliver and slice credentials.
205     #
206     # @param ticket_str the string representation of a ticket object
207
208     def redeem_ticket(self, ticket_str):
209         self.decode_ticket(ticket_str)
210         ticket = self.client_ticket
211
212         print "ticket received for", self.object_gid.get_hrn()
213
214         pt = self.geni_ticket_to_plc_ticket(ticket)
215
216         print "plticket", pt
217
218         str = xmlrpclib.dumps((pt,), allow_none=True)
219         self.nodemanager.AdminTicket(str)
220
221         # TODO: should return a sliver credential
222
223     # ------------------------------------------------------------------------
224     # Slice Interface
225
226     ##
227     # List the slices on a component.
228     #
229     # @param cred_str string representation of a credential object that
230     #     authorizes the caller
231     #
232     # @return a list of slice names
233
234     def list_slices(self, cred_str):
235         self.decode_authentication(cred_str, "listslices")
236         slice_names = self.nodemanager.GetXIDs().keys()
237         return slice_names
238
239     # ------------------------------------------------------------------------
240     # Management Interface
241
242     ##
243     # Reboot the component.
244     #
245     # @param cred_str string representation of a credential object that
246     #     authorizes the caller
247
248     def reboot(self, cred_str):
249         self.decode_authentication(cred_str, "reboot")
250         system("/sbin/reboot")
251
252
253 if __name__ == "__main__":
254     global TrustedRoots
255
256     key_file = "component.key"
257     cert_file = "component.cert"
258
259     # if no key is specified, then make one up
260     if (not os.path.exists(key_file)) or (not os.path.exists(cert_file)):
261         key = Keypair(create=True)
262         key.save_to_file(key_file)
263
264         cert = Certificate(subject="component")
265         cert.set_issuer(key=key, subject="component")
266         cert.set_pubkey(key)
267         cert.sign()
268         cert.save_to_file(cert_file)
269
270     TrustedRoots = TrustedRootList()
271
272     s = ComponentManager("", 12345, key_file, cert_file)
273     s.trusted_cert_list = TrustedRoots.get_list()
274     s.run()
275