3 This module exposes an XMLRPC interface that allows PlanetLab users to
4 create/destroy slivers with delegated instantiation, start and stop
5 slivers, make resource loans, and examine resource allocations. The
6 XMLRPC is provided on a localhost-only TCP port as well as via a Unix
7 domain socket that is accessible by ssh-ing into a delegate account
8 with the forward_api_calls shell.
11 import SimpleXMLRPCServer
22 from PLC.Parameter import Parameter, Mixed
29 # TODO: These try/excepts are a hack to allow doc/DocBookLocal.py to
30 # import this file in order to extrac the documentation from each
31 # exported function. A better approach will involve more extensive code
34 except: import logger as database
36 except: import logger as sliver_vs
37 import ticket as ticket_module
44 def export_to_api(nargs):
46 nargs_dict[method.__name__] = nargs
47 api_method_dict[method.__name__] = method
51 def export_to_docbook(**kwargs):
64 # Inspect method. Remove self from the argument list.
65 max_args = method.func_code.co_varnames[0:method.func_code.co_argcount]
66 defaults = method.func_defaults
69 min_args = max_args[0:len(max_args) - len(defaults)]
71 defaults = tuple([None for arg in min_args]) + defaults
72 return (min_args, max_args, defaults)
74 keywords['name'] = method.__name__
75 keywords['args'] = args
77 method.__setattr__(arg, keywords[arg])
80 method.__setattr__(arg, kwargs[arg])
91 @export_to_docbook(roles=['self'],
93 returns=Parameter([], 'A list of supported functions'))
96 """Get a list of functions currently supported by the Node Manager API"""
97 return ''.join([method.__doc__ + '\n' for method in api_method_dict.itervalues()])
99 @export_to_docbook(roles=['self'],
100 accepts=[Parameter(str, 'A ticket returned from GetSliceTicket()')],
101 returns=Parameter(int, '1 if successful'))
104 """The Node Manager periodically polls the PLC API for a list of all
105 slices that are allowed to exist on the given node. Before
106 actions are performed on a delegated slice (such as creation),
107 a controller slice must deliver a valid slice ticket to NM.
109 This ticket is the value retured by PLC's GetSliceTicket() API call,
112 data = ticket_module.verify(ticket)
113 name = data['slivers'][0]['name']
116 logger.log('Ticket delivered for %s' % name)
117 Create(database.db.get(name))
118 except Exception, err:
119 raise xmlrpclib.Fault(102, 'Ticket error: ' + str(err))
121 @export_to_docbook(roles=['self'],
123 returns={'sliver_name' : Parameter(int, 'the associated xid')})
126 """Return an dictionary mapping Slice names to XIDs"""
127 return dict([(pwent[0], pwent[2]) for pwent in pwd.getpwall() if pwent[6] == sliver_vs.Sliver_VS.SHELL])
129 @export_to_docbook(roles=['self'],
131 returns={ 'sliver_name' : Parameter(str, 'the associated SSHKey')})
134 """Return an dictionary mapping slice names to SSH keys"""
136 for rec in database.db.itervalues():
138 keydict[rec['name']] = rec['keys']
141 @export_to_docbook(roles=['nm-controller', 'self'],
142 accepts=[Parameter(str, 'A sliver/slice name.')],
143 returns=Parameter(int, '1 if successful'))
145 def Create(sliver_name):
146 """Create a non-PLC-instantiated sliver"""
148 if rec['instantiation'] == 'delegated': accounts.get(rec['name']).ensure_created(rec)
150 @export_to_docbook(roles=['nm-controller', 'self'],
151 accepts=[Parameter(str, 'A sliver/slice name.')],
152 returns=Parameter(int, '1 if successful'))
154 def Destroy(sliver_name):
155 """Destroy a non-PLC-instantiated sliver"""
157 if rec['instantiation'] == 'delegated': accounts.get(rec['name']).ensure_destroyed()
159 @export_to_docbook(roles=['nm-controller', 'self'],
160 accepts=[Parameter(str, 'A sliver/slice name.')],
161 returns=Parameter(int, '1 if successful'))
163 def Start(sliver_name):
164 """Run start scripts belonging to the specified sliver"""
166 accounts.get(rec['name']).start()
168 @export_to_docbook(roles=['nm-controller', 'self'],
169 accepts=[Parameter(str, 'A sliver/slice name.')],
170 returns=Parameter(int, '1 if successful'))
172 def Stop(sliver_name):
173 """Kill all processes belonging to the specified sliver"""
175 accounts.get(rec['name']).stop()
177 @export_to_docbook(roles=['nm-controller', 'self'],
178 accepts=[Parameter(str, 'A sliver/slice name.')],
179 returns=Parameter(dict, "A resource specification"))
181 def GetEffectiveRSpec(sliver_name):
182 """Return the RSpec allocated to the specified sliver, including loans"""
184 return rec.get('_rspec', {}).copy()
186 @export_to_docbook(roles=['nm-controller', 'self'],
187 accepts=[Parameter(str, 'A sliver/slice name.')],
189 "resource name" : Parameter(int, "amount")
193 def GetRSpec(sliver_name):
194 """Return the RSpec allocated to the specified sliver, excluding loans"""
196 return rec.get('rspec', {}).copy()
198 @export_to_docbook(roles=['nm-controller', 'self'],
199 accepts=[Parameter(str, 'A sliver/slice name.')],
200 returns=[Mixed(Parameter(str, 'recipient slice name'),
201 Parameter(str, 'resource name'),
202 Parameter(int, 'resource amount'))]
205 def GetLoans(sliver_name):
206 """Return the list of loans made by the specified sliver"""
208 return rec.get('_loans', [])[:]
210 def validate_loans(obj):
211 """Check that <obj> is a valid loan specification."""
212 def validate_loan(obj): return (type(obj)==list or type(obj)==tuple) and len(obj)==3 and type(obj[0])==str and type(obj[1])==str and obj[1] in database.LOANABLE_RESOURCES and type(obj[2])==int and obj[2]>=0
213 return type(obj)==list and False not in map(validate_loan, obj)
215 @export_to_docbook(roles=['nm-controller', 'self'],
216 accepts=[ Parameter(str, 'A sliver/slice name.'),
217 [Mixed(Parameter(str, 'recipient slice name'),
218 Parameter(str, 'resource name'),
219 Parameter(int, 'resource amount'))] ],
220 returns=Parameter(int, '1 if successful'))
222 def SetLoans(sliver_name, loans):
223 """Overwrite the list of loans made by the specified sliver.
225 Also, note that SetLoans will not throw an error if more capacity than the
226 RSpec is handed out, but it will silently discard those loans that would
227 put it over capacity. This behavior may be replaced with error semantics
228 in the future. As well, there is currently no asynchronous notification
229 of loss of resources.
232 if not validate_loans(loans): raise xmlrpclib.Fault(102, 'Invalid argument: the second argument must be a well-formed loan specification')
233 rec['_loans'] = loans