4 This module exposes an XMLRPC interface that allows PlanetLab users to
5 create/destroy slivers with delegated instantiation, start and stop
6 slivers, make resource loans, and examine resource allocations. The
7 XMLRPC is provided on a localhost-only TCP port as well as via a Unix
8 domain socket that is accessible by ssh-ing into a delegate account
9 with the forward_api_calls shell.
12 import SimpleXMLRPCServer
24 from PLC.Parameter import Parameter, Mixed
26 def Parameter(a = None, b = None): pass
27 def Mixed(a = None, b = None, c = None): pass
33 # TODO: These try/excepts are a hack to allow doc/DocBookLocal.py to
34 # import this file in order to extract the documentation from each
36 # A better approach will involve more extensive code splitting, I think.
38 except: import logger as database
39 #try: import sliver_vs
40 #except: import logger as sliver_vs
41 import ticket as ticket_module
44 deliver_ticket = None # set in slivermanager.start()
49 def export_to_api(nargs):
51 nargs_dict[method.__name__] = nargs
52 api_method_dict[method.__name__] = method
56 def export_to_docbook(**kwargs):
69 # Inspect method. Remove self from the argument list.
70 max_args = method.func_code.co_varnames[0:method.func_code.co_argcount]
71 defaults = method.func_defaults
74 min_args = max_args[0:len(max_args) - len(defaults)]
76 defaults = tuple([None for arg in min_args]) + defaults
77 return (min_args, max_args, defaults)
79 keywords['name'] = method.__name__
80 keywords['args'] = args
82 method.__setattr__(arg, keywords[arg])
85 method.__setattr__(arg, kwargs[arg])
96 @export_to_docbook(roles=['self'],
98 returns=Parameter([], 'A list of supported functions'))
101 """Get a list of functions currently supported by the Node Manager API"""
102 names=api_method_dict.keys()
104 return ''.join(['**** ' + api_method_dict[name].__name__ + '\n' + api_method_dict[name].__doc__ + '\n'
107 @export_to_docbook(roles=['self'],
108 accepts=[Parameter(str, 'A ticket returned from GetSliceTicket()')],
109 returns=Parameter(int, '1 if successful'))
112 """The Node Manager periodically polls the PLC API for a list of all
113 slices that are allowed to exist on the given node. Before
114 actions are performed on a delegated slice (such as creation),
115 a controller slice must deliver a valid slice ticket to NM.
117 This ticket is the value retured by PLC's GetSliceTicket() API call."""
119 data = ticket_module.verify(ticket)
120 name = data['slivers'][0]['name']
123 logger.log('api_calls: Ticket delivered for %s' % name)
124 Create(database.db.get(name))
125 except Exception, err:
126 raise xmlrpclib.Fault(102, 'Ticket error: ' + str(err))
128 @export_to_docbook(roles=['self'],
129 accepts=[Parameter(str, 'A ticket returned from GetSlivers()')],
130 returns=Parameter(int, '1 if successful'))
132 def AdminTicket(ticket):
133 """Admin interface to create slivers based on ticket returned by GetSlivers()."""
135 data, = xmlrpclib.loads(ticket)[0]
136 name = data['slivers'][0]['name']
139 logger.log('api_calls: Admin Ticket delivered for %s' % name)
140 Create(database.db.get(name))
141 except Exception, err:
142 raise xmlrpclib.Fault(102, 'Ticket error: ' + str(err))
145 @export_to_docbook(roles=['self'],
147 returns={'sliver_name' : Parameter(int, 'the associated xid')})
150 """Return an dictionary mapping Slice names to XIDs"""
151 return dict([(pwent[0], pwent[2]) for pwent in pwd.getpwall() if pwent[6] == sliver_lxc.Sliver_LXC.SHELL])
153 @export_to_docbook(roles=['self'],
155 returns={ 'sliver_name' : Parameter(str, 'the associated SSHKey')})
158 """Return an dictionary mapping slice names to SSH keys"""
160 for rec in database.db.itervalues():
162 keydict[rec['name']] = rec['keys']
166 @export_to_docbook(roles=['nm-controller', 'self'],
167 accepts=[Parameter(str, 'A sliver/slice name.')],
168 returns=Parameter(int, '1 if successful'))
170 def Create(sliver_name):
171 """Create a non-PLC-instantiated sliver"""
173 if rec['instantiation'] == 'delegated':
174 accounts.get(rec['name']).ensure_created(rec)
175 logger.log("api_calls: Create %s"%rec['name'])
177 raise Exception, "Only PLC can create non delegated slivers."
180 @export_to_docbook(roles=['nm-controller', 'self'],
181 accepts=[Parameter(str, 'A sliver/slice name.')],
182 returns=Parameter(int, '1 if successful'))
184 def Destroy(sliver_name):
185 """Destroy a non-PLC-instantiated sliver"""
187 if rec['instantiation'] == 'delegated':
188 accounts.get(rec['name']).ensure_destroyed()
189 logger.log("api_calls: Destroy %s"%rec['name'])
191 raise Exception, "Only PLC can destroy non delegated slivers."
194 @export_to_docbook(roles=['nm-controller', 'self'],
195 accepts=[Parameter(str, 'A sliver/slice name.')],
196 returns=Parameter(int, '1 if successful'))
198 def Start(sliver_name):
199 """Configure and start sliver."""
201 accounts.get(rec['name']).start(rec)
202 logger.log("api_calls: Start %s"%rec['name'])
205 @export_to_docbook(roles=['nm-controller', 'self'],
206 accepts=[Parameter(str, 'A sliver/slice name.')],
207 returns=Parameter(int, '1 if successful'))
209 def Stop(sliver_name):
210 """Kill all processes belonging to the specified sliver"""
212 accounts.get(rec['name']).stop()
213 logger.log("api_calls: Stop %s"%rec['name'])
216 @export_to_docbook(roles=['nm-controller', 'self'],
217 accepts=[Parameter(str, 'A sliver/slice name.')],
218 returns=Parameter(int, '1 if successful'))
220 def ReCreate(sliver_name):
221 """Stop, Destroy, Create, Start sliver in order to reinstall it."""
223 accounts.get(rec['name']).stop()
224 accounts.get(rec['name']).ensure_created(rec)
225 accounts.get(rec['name']).start(rec)
226 logger.log("api_calls: ReCreate %s"%rec['name'])
228 @export_to_docbook(roles=['nm-controller', 'self'],
229 accepts=[Parameter(str, 'A sliver/slice name.')],
230 returns=Parameter(dict, "A resource specification"))
232 def GetEffectiveRSpec(sliver_name):
233 """Return the RSpec allocated to the specified sliver, including loans"""
235 return rec.get('_rspec', {}).copy()
238 @export_to_docbook(roles=['nm-controller', 'self'],
239 accepts=[Parameter(str, 'A sliver/slice name.')],
240 returns={"resource name" : Parameter(int, "amount")})
242 def GetRSpec(sliver_name):
243 """Return the RSpec allocated to the specified sliver, excluding loans"""
245 return rec.get('rspec', {}).copy()
248 @export_to_docbook(roles=['nm-controller', 'self'],
249 accepts=[Parameter(str, 'A sliver/slice name.')],
250 returns=[Mixed(Parameter(str, 'recipient slice name'),
251 Parameter(str, 'resource name'),
252 Parameter(int, 'resource amount'))])
255 def GetLoans(sliver_name):
256 """Return the list of loans made by the specified sliver"""
258 return rec.get('_loans', [])[:]
260 def validate_loans(loans):
261 """Check that <obj> is a list of valid loan specifications."""
262 def validate_loan(loan):
263 return (type(loan)==list or type(loan)==tuple) and len(loan)==3 \
264 and type(loan[0])==str and type(loan[1])==str and loan[1] in database.LOANABLE_RESOURCES and type(loan[2])==int and loan[2]>=0
265 return type(loans)==list and False not in [validate_loan(load) for loan in loans]
268 @export_to_docbook(roles=['nm-controller', 'self'],
269 accepts=[ Parameter(str, 'A sliver/slice name.'),
270 [Mixed(Parameter(str, 'recipient slice name'),
271 Parameter(str, 'resource name'),
272 Parameter(int, 'resource amount'))], ],
273 returns=Parameter(int, '1 if successful'))
275 def SetLoans(sliver_name, loans):
276 """Overwrite the list of loans made by the specified sliver.
278 Also, note that SetLoans will not throw an error if more capacity than the
279 RSpec is handed out, but it will silently discard those loans that would
280 put it over capacity. This behavior may be replaced with error semantics
281 in the future. As well, there is currently no asynchronous notification
282 of loss of resources."""
284 if not validate_loans(loans):
285 raise xmlrpclib.Fault(102, 'Invalid argument: the second argument must be a well-formed loan specification')
286 rec['_loans'] = loans
289 @export_to_docbook(roles=['nm-controller', 'self'],
290 returns=Parameter(dict, 'Record dictionary'))
292 def GetRecord(sliver_name):
293 """Return sliver record"""