ReCreate is NOT an anonymous call.
[nodemanager.git] / api.py
1 """Sliver manager API.
2
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.
9 """
10
11 import SimpleXMLRPCServer
12 import SocketServer
13 import errno
14 import os
15 import pwd
16 import socket
17 import struct
18 import threading
19 import xmlrpclib
20 import sys
21
22 import accounts
23 import database
24 import logger
25 import sliver_vs
26 import ticket
27 import tools
28 from api_calls import *
29 try:
30     sys.path.append("/etc/planetlab")
31     from plc_config import *
32 except:
33     logger.log("api:  Warning: Configuration file /etc/planetlab/plc_config.py not found", 2)
34     PLC_SLICE_PREFIX="pl"
35     logger.log("api:  Warning: admin slice prefix set to %s" %(PLC_SLICE_PREFIX), 2)
36
37 API_SERVER_PORT = 812
38 UNIX_ADDR = '/tmp/sliver_mgr.api'
39
40 class APIRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler):
41     # overriding _dispatch to achieve this effect is officially deprecated,
42     # but I can't figure out how to get access to .request without
43     # duplicating SimpleXMLRPCServer code here, which is more likely to
44     # change than the deprecated behavior is to be broken
45
46     @database.synchronized
47     def _dispatch(self, method_name_unicode, args):
48         method_name = str(method_name_unicode)
49         try: method = api_method_dict[method_name]
50         except KeyError:
51             api_method_list = api_method_dict.keys()
52             api_method_list.sort()
53             raise xmlrpclib.Fault(100, 'Invalid API method %s.  Valid choices are %s' % \
54                 (method_name, ', '.join(api_method_list)))
55         expected_nargs = nargs_dict[method_name]
56         if len(args) != expected_nargs: 
57             raise xmlrpclib.Fault(101, 'Invalid argument count: got %d, expecting %d.' % \
58                 (len(args), expected_nargs))
59         else:
60             # Figure out who's calling.
61             # XXX - these ought to be imported directly from some .h file
62             SO_PEERCRED = 17
63             sizeof_struct_ucred = 12
64             ucred = self.request.getsockopt(socket.SOL_SOCKET, SO_PEERCRED, sizeof_struct_ucred)
65             xid = struct.unpack('3i', ucred)[2]
66             caller_name = pwd.getpwuid(xid)[0]
67             # Special case the genicw
68             if method_name == "AdminTicket":
69                 if caller_name == PLC_SLICE_PREFIX+"_genicw":
70                     try: result = method(*args)
71                     except Exception, err: raise xmlrpclib.Fault(104, 'Error in call: %s' %err)
72                 else:
73                     raise xmlrpclib.Fault(108, '%s: Permission denied.' % caller_name)
74             # Anyone can call these functions
75             elif method_name not in ('Help', 'Ticket', 'GetXIDs', 'GetSSHKeys'):
76                 # Authenticate the caller if not in the above fncts.
77                 target_name = args[0]
78                 # Gather target slice's object.
79                 target_rec = database.db.get(target_name)
80                 # only work on slivers.  Sannity check.
81                 if not (target_rec and target_rec['type'].startswith('sliver.')): 
82                     raise xmlrpclib.Fault(102, \
83                         'Invalid argument: the first argument must be a sliver name.')
84                 # only manipulate slivers who delegate you authority 
85                 if caller_name in (target_name, target_rec['delegations']):
86                     try: result = method(target_rec, *args[1:])
87                     except Exception, err: raise xmlrpclib.Fault(104, 'Error in call: %s' %err)
88                 else:
89                     raise xmlrpclib.Fault(108, '%s: Permission denied.' % caller_name)
90             else: # Execute anonymous call. 
91                 try: result = method(*args)
92                 except Exception, err: raise xmlrpclib.Fault(104, 'Error in call: %s' %err)
93             if result == None: result = 1
94             return result
95
96 class APIServer_INET(SocketServer.ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer): allow_reuse_address = True
97
98 class APIServer_UNIX(APIServer_INET): address_family = socket.AF_UNIX
99
100 def start():
101     """Start two XMLRPC interfaces: one bound to localhost, the other bound to a Unix domain socket."""
102     serv1 = APIServer_INET(('127.0.0.1', API_SERVER_PORT), requestHandler=APIRequestHandler, logRequests=0)
103     tools.as_daemon_thread(serv1.serve_forever)
104     try: os.unlink(UNIX_ADDR)
105     except OSError, e:
106         if e.errno != errno.ENOENT: raise
107     serv2 = APIServer_UNIX(UNIX_ADDR, requestHandler=APIRequestHandler, logRequests=0)
108     tools.as_daemon_thread(serv2.serve_forever)
109     os.chmod(UNIX_ADDR, 0666)