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