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