pl_genicw is allowed to call all methods
[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)[1]
66             caller_name = pwd.getpwuid(xid)[0]
67             # Special case the genicw
68             if caller_name == PLC_SLICE_PREFIX+"_genicw":
69                 try: result = method(*args)
70                 except Exception, err: raise xmlrpclib.Fault(104, 'Error in call: %s' %err)
71             # Anyone can call these functions
72             elif method_name not in ('Help', 'Ticket', 'GetXIDs', 'GetSSHKeys'):
73                 # Authenticate the caller if not in the above fncts.
74                 if method_name == "GetRecord":
75                     target_name = caller_name
76                 else:
77                     target_name = args[0]
78
79                 # Gather target slice's object.
80                 target_rec = database.db.get(target_name)
81
82                 # only work on slivers or self.  Sannity check.
83                 if not (target_rec and target_rec['type'].startswith('sliver.')): 
84                     raise xmlrpclib.Fault(102, \
85                         'Invalid argument: the first argument must be a sliver name.')
86
87                 # only manipulate slivers who delegate you authority 
88                 if caller_name in (target_name, target_rec['delegations']):
89                     try: result = method(target_rec, *args[1:])
90                     except Exception, err: raise xmlrpclib.Fault(104, 'Error in call: %s' %err)
91                 else:
92                     raise xmlrpclib.Fault(108, '%s: Permission denied.' % caller_name)
93             else: # Execute anonymous call. 
94                 try: result = method(*args)
95                 except Exception, err: raise xmlrpclib.Fault(104, 'Error in call: %s' %err)
96             if result == None: result = 1
97             return result
98
99 class APIServer_INET(SocketServer.ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer): allow_reuse_address = True
100
101 class APIServer_UNIX(APIServer_INET): address_family = socket.AF_UNIX
102
103 def start():
104     """Start two XMLRPC interfaces: one bound to localhost, the other bound to a Unix domain socket."""
105     serv1 = APIServer_INET(('127.0.0.1', API_SERVER_PORT), requestHandler=APIRequestHandler, logRequests=0)
106     tools.as_daemon_thread(serv1.serve_forever)
107     try: os.unlink(UNIX_ADDR)
108     except OSError, e:
109         if e.errno != errno.ENOENT: raise
110     serv2 = APIServer_UNIX(UNIX_ADDR, requestHandler=APIRequestHandler, logRequests=0)
111     tools.as_daemon_thread(serv2.serve_forever)
112     os.chmod(UNIX_ADDR, 0666)