nicer layout; no change but for info() messages to back down as debug()
[sfa.git] / sfa / planetlab / plshell.py
1 import sys
2 import xmlrpclib
3 import socket
4 from urlparse import urlparse
5
6 from sfa.util.sfalogging import logger
7
8
9 class PlShell:
10     """
11     A simple xmlrpc shell to a myplc instance
12     This class can receive all PLCAPI calls to the underlying testbed
13     For safety this is limited to a set of hard-coded calls
14     """
15
16     direct_calls = [
17         'AddNode', 'AddPerson', 'AddPersonKey', 'AddPersonToSite',
18         'AddPersonToSlice', 'AddRoleToPerson', 'AddSite', 'AddSiteTag', 'AddSlice',
19         'AddSliceTag', 'AddSliceToNodes', 'BindObjectToPeer', 'DeleteKey',
20         'DeleteNode', 'DeletePerson', 'DeletePersonFromSlice', 'DeleteSite',
21         'DeleteSlice', 'DeleteSliceFromNodes', 'DeleteSliceTag', 'GetInitScripts',
22         'GetInterfaces', 'GetKeys', 'GetNodeTags', 'GetPeers',
23         'GetPersons', 'GetSlices', 'GetSliceTags', 'GetTagTypes',
24         'UnBindObjectFromPeer', 'UpdateNode', 'UpdatePerson', 'UpdateSite',
25         'UpdateSlice', 'UpdateSliceTag',
26         # also used as-is in importer
27         'GetSites', 'GetNodes', 'GetSiteTags',
28         # Lease management methods
29         'GetLeases', 'GetLeaseGranularity', 'DeleteLeases', 'UpdateLeases',
30         'AddLeases',
31         # HRN management methods
32         'SetPersonHrn', 'GetPersonHrn', 'SetSliceHrn', 'GetSliceHrn',
33         'SetNodeHrn', 'GetNodeHrn', 'GetSiteHrn', 'SetSiteHrn',
34         # Tag slice/person/site created by SFA
35         'SetPersonSfaCreated', 'GetPersonSfaCreated', 'SetSliceSfaCreated',
36         'GetSliceSfaCreated', 'SetNodeSfaCreated', 'GetNodeSfaCreated',
37         'GetSiteSfaCreated', 'SetSiteSfaCreated',
38     ]
39     # support for other names - this is experimental
40     alias_calls = {
41         'get_authorities': 'GetSites',
42         'get_nodes': 'GetNodes',
43     }
44
45     # use the 'capability' auth mechanism for higher performance when the PLC
46     # db is local
47     def __init__(self, config):
48         url = config.SFA_PLC_URL
49         # try to figure if the url is local
50         hostname = urlparse(url).hostname
51         is_local = False
52         if hostname == 'localhost':
53             is_local = True
54         # otherwise compare IP addresses;
55         # this might fail for any number of reasons, so let's harden that
56         try:
57             # xxx todo this seems to result in a DNS request for each incoming request to the AM
58             # should be cached or improved
59             url_ip = socket.gethostbyname(hostname)
60             local_ip = socket.gethostbyname(socket.gethostname())
61             if url_ip == local_ip:
62                 is_local = True
63         except:
64             pass
65
66         if is_local:
67             try:
68                 # too bad this is not installed properly
69                 plcapi_path = "/usr/share/plc_api"
70                 if plcapi_path not in sys.path:
71                     sys.path.append(plcapi_path)
72                 import PLC.Shell
73                 plc_direct_access = True
74             except:
75                 plc_direct_access = False
76         if is_local and plc_direct_access:
77             logger.debug('plshell access - capability')
78             self.plauth = {
79                 'AuthMethod': 'capability',
80                 'Username': str(config.SFA_PLC_USER),
81                 'AuthString': str(config.SFA_PLC_PASSWORD),
82             }
83             self.proxy = PLC.Shell.Shell()
84
85         else:
86             logger.debug('plshell access - xmlrpc')
87             self.plauth = {
88                 'AuthMethod': 'password',
89                 'Username':   str(config.SFA_PLC_USER),
90                 'AuthString': str(config.SFA_PLC_PASSWORD),
91             }
92             self.proxy = xmlrpclib.Server(url, verbose=False, allow_none=True)
93
94     def __getattr__(self, name):
95         def func(*args, **kwds):
96             actual_name = None
97             if name in PlShell.direct_calls:
98                 actual_name = name
99             if name in PlShell.alias_calls:
100                 actual_name = PlShell.alias_calls[name]
101             if not actual_name:
102                 raise Exception(
103                     "Illegal method call %s for PL driver" % (name))
104             result = getattr(self.proxy, actual_name)(
105                 self.plauth, *args, **kwds)
106             logger.debug('PlShell %s (%s) returned ... ' % (name, actual_name))
107             return result
108         return func