2 # Aggregate is a GeniServer that implements the Slice interface at PLC
9 from util.hierarchy import Hierarchy
10 from util.trustedroot import TrustedRootList
11 from util.cert import Keypair, Certificate
12 from util.gid import GID
13 from util.geniserver import GeniServer
14 from util.record import GeniRecord
15 from util.genitable import GeniTable
16 from util.geniticket import Ticket
17 from util.excep import *
18 from util.misc import *
21 # Aggregate class extends GeniServer class
23 class Aggregate(GeniServer):
25 # Create a new aggregate object.
27 # @param ip the ip address to listen on
28 # @param port the port to listen on
29 # @param key_file private key filename of registry
30 # @param cert_file certificate filename containing public key (could be a GID file)
32 def __init__(self, ip, port, key_file, cert_file):
33 GeniServer.__init__(self, ip, port, key_file, cert_file)
35 # get PL account settings from config module
36 self.pl_auth = get_pl_auth()
38 # connect to planetlab
39 if "Url" in self.pl_auth:
40 self.connect_remote_shell()
42 self.connect_local_shell()
45 # Connect to a remote shell via XMLRPC
47 def connect_remote_shell(self):
49 self.shell = remoteshell.RemoteShell()
52 # Connect to a local shell via local API functions
54 def connect_local_shell(self):
56 self.shell = PLC.Shell.Shell(globals = globals())
59 # Register the server RPCs for the slice interface
61 def register_functions(self):
62 GeniServer.register_functions(self)
63 self.server.register_function(self.create_slice)
64 self.server.register_function(self.get_ticket)
65 self.server.register_function(self.redeem_ticket)
66 self.server.register_function(self.start_slice)
67 self.server.register_function(self.stop_slice)
68 self.server.register_function(self.reset_slice)
69 self.server.register_function(self.delete_slice)
70 self.server.register_function(self.get_slice_resources)
71 self.server.register_function(self.list_slices)
72 self.server.register_function(self.list_nodes)
75 # Given an authority name, return the information for that authority. This
76 # is basically a stub that calls the hierarchy module.
78 # @param auth_hrn human readable name of authority
80 def get_auth_info(self, auth_hrn):
81 return AuthHierarchy.get_auth_info(auth_hrn)
84 # Given an authority name, return the database table for that authority. If
85 # the database table does not exist, then one will be automatically
88 # @param auth_name human readable name of authority
90 def get_auth_table(self, auth_name):
91 auth_info = self.get_auth_info(auth_name)
93 table = GeniTable(hrn=auth_name,
94 cninfo=auth_info.get_dbinfo())
96 # if the table doesn't exist, then it means we haven't put any records
97 # into this authority yet.
99 if not table.exists():
100 report.trace("Registry: creating table for authority " + auth_name)
106 # Verify that an authority belongs to this registry. This is basically left
107 # up to the implementation of the hierarchy module. If the specified name
108 # does not belong to this registry, an exception is thrown indicating the
109 # caller should contact someone else.
111 # @param auth_name human readable name of authority
113 def verify_auth_belongs_to_me(self, name):
114 # get_auth_info will throw an exception if the authority does not
116 self.get_auth_info(name)
119 # Verify that an object belongs to this registry. By extension, this implies
120 # that the authority that owns the object belongs to this registry. If the
121 # object does not belong to this registry, then an exception is thrown.
123 # @param name human readable name of object
125 def verify_object_belongs_to_me(self, name):
126 auth_name = get_authority(name)
128 # the root authority belongs to the registry by default?
129 # TODO: is this true?
131 self.verify_auth_belongs_to_me(auth_name)
134 # Verify that the object_gid that was specified in the credential allows
135 # permission to the object 'name'. This is done by a simple prefix test.
136 # For example, an object_gid for planetlab.us.arizona would match the
137 # objects planetlab.us.arizona.slice1 and planetlab.us.arizona.
139 # @param name human readable name to test
141 def verify_object_permission(self, name):
142 object_hrn = self.object_gid.get_hrn()
143 if object_hrn == name:
145 if name.startswith(object_hrn + "."):
147 raise PermissionError(name)
150 # Convert a PLC record into the slice information that will be stored in
151 # a ticket. There are two parts to this information: attributes and
154 # Attributes are non-resource items, such as keys and the initscript
155 # Rspec is a set of resource specifications
157 # @param record a record object
159 # @return a tuple (attrs, rspec) of dictionaries
161 def record_to_slice_info(self, record):
163 # get the user keys from the slice
165 persons = self.shell.GetPersons(self.pl_auth, record.pl_info['person_ids'])
166 for person in persons:
167 person_keys = self.shell.GetKeys(self.pl_auth, person["key_ids"])
168 for person_key in person_keys:
169 keys = keys + [person_key['key']]
172 attributes['name'] = record.pl_info['name']
173 attributes['keys'] = keys
174 attributes['instantiation'] = record.pl_info['instantiation']
175 attributes['vref'] = 'default'
176 attributes['timestamp'] = time.time()
177 attributes['slice_id'] = record.pl_info['slice_id']
181 # get the PLC attributes and separate them into slice attributes and
184 filter['slice_id'] = record.pl_info['slice_id']
185 plc_attrs = self.shell.GetSliceAttributes(self.pl_auth, filter)
186 for attr in plc_attrs:
189 # initscripts: lookup the contents of the initscript and store it
190 # in the ticket attributes
191 if (name == "initscript"):
192 filter={'name': attr['value']}
193 initscripts = self.shell.GetInitScripts(self.pl_auth, filter)
195 attributes['initscript'] = initscripts[0]['script']
197 rspec[name] = attr['value']
199 return (attributes, rspec)
202 # create_slice: Create (instantiate) a slice.
204 # @param cred credential string
205 # @param name name of the slice to retrieve a ticket for
206 # @param rspec resource specification dictionary
208 # @return the string representation of a ticket object
210 def create_slice(self, cred, name, rspec):
211 self.decode_authentication(cred, "createslice")
212 slicename = hrn_to_pl_slicename(self.object_gid.get_hrn())
213 # TODO: create a slice
216 # get_ticket: Retrieve a ticket.
218 # This operation is currently implemented on PLC only (see SFA,
219 # engineering decisions); it is not implemented on components.
221 # The ticket is filled in with information from the PLC database. This
222 # information includes resources, and attributes such as user keys and
225 # @param cred credential string
226 # @param name name of the slice to retrieve a ticket for
227 # @param rspec resource specification dictionary
229 # @return the string representation of a ticket object
231 def get_ticket(self, cred, name, rspec):
232 self.decode_authentication(cred, "getticket")
234 self.verify_object_belongs_to_me(name)
236 self.verify_object_permission(name)
238 auth_hrn = get_authority(name)
239 auth_info = self.get_auth_info(auth_hrn)
241 records = self.resolve_raw("slice", name, must_exist=True)
244 object_gid = record.get_gid_object()
245 new_ticket = Ticket(subject = object_gid.get_subject())
246 new_ticket.set_gid_caller(self.client_gid)
247 new_ticket.set_gid_object(object_gid)
248 new_ticket.set_issuer(key=auth_info.get_pkey_object(), subject=auth_hrn)
249 new_ticket.set_pubkey(object_gid.get_pubkey())
251 self.fill_record_info(record)
253 (attributes, rspec) = self.record_to_slice_info(record)
255 new_ticket.set_attributes(attributes)
256 new_ticket.set_rspec(rspec)
258 new_ticket.set_parent(AuthHierarchy.get_auth_ticket(auth_hrn))
263 return new_ticket.save_to_string(save_parents=True)
266 # redeem_ticket: Redeem a ticket.
268 # Not supported at a PLC aggregate.
270 # @param ...not sure...
272 def redeem_ticket(self, whatever):
276 # stop_slice: Stop a slice.
278 # @param cred a credential identifying the caller (callerGID) and the slice
281 def stop_slice(self, cred_str):
282 self.decode_authentication(cred_str, "stopslice")
283 slicename = hrn_to_pl_slicename(self.object_gid.get_hrn())
284 # TODO: stop the slice
287 # start_slice: Start a slice.
289 # @param cred a credential identifying the caller (callerGID) and the slice
292 def start_slice(self, cred_str):
293 self.decode_authentication(cred_str, "startslice")
294 slicename = hrn_to_pl_slicename(self.object_gid.get_hrn())
295 # TODO: start the slice
298 # reset_slice: Reset a slice.
300 # @param cred a credential identifying the caller (callerGID) and the slice
303 def reset_slice(self, cred_str):
304 self.decode_authentication(cred_str, "resetslice")
305 slicename = hrn_to_pl_slicename(self.object_gid.get_hrn())
306 # TODO: reset the slice
309 # delete_slice: Delete a slice.
311 # @param cred a credential identifying the caller (callerGID) and the slice
314 def delete_slice(self, cred_str):
315 self.decode_authentication(cred_str, "deleteslice")
316 slicename = hrn_to_pl_slicename(self.object_gid.get_hrn())
317 # TODO: delete the slice
320 # get_resources: Get resources allocated to slice
322 # @param cred a credential identifying the caller (callerGID) and the slice
325 def get_slice_resources(self, cred_str):
326 self.decode_authentication(cred_str, "getsliceresources")
327 slicename = hrn_to_pl_slicename(self.object_gid.get_hrn())
328 # TODO: get resources allocated to slice
331 # list_slices: List hosted slices.
333 # @param cred a credential identifying the caller (callerGID)
335 def list_slices(self, cred_str):
336 self.decode_authentication(cred_str, "listslices")
337 # TODO: list hosted slices
340 # list_nodes: List available nodes.
342 # @param cred a credential identifying the caller (callerGID)
344 def list_nodes(self, cred_str):
345 self.decode_authentication(cred_str, "listnodes")
346 # TODO: list available nodes