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