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