more pep8-friendly
[sfa.git] / sfa / server / sfaapi.py
1 # pylint: disable=c0111
2
3 import os
4 import os.path
5 import datetime
6
7 from sfa.util.faults import SfaFault, RecordNotFound
8 from sfa.util.genicode import GENICODE
9 from sfa.util.config import Config
10 from sfa.util.cache import Cache
11
12 from sfa.trust.auth import Auth
13 from sfa.trust.certificate import Keypair, Certificate
14 from sfa.trust.credential import Credential
15 from sfa.trust.rights import determine_rights
16 from sfa.util.version import version_core
17 from sfa.server.xmlrpcapi import XmlrpcApi
18 from sfa.client.return_value import ReturnValue
19
20 from sfa.storage.alchemy import alchemy
21
22 ####################
23
24
25 class SfaApi(XmlrpcApi):
26     """
27     An SfaApi instance is a basic xmlrpc service
28     augmented with the local cryptographic material and hrn
29
30     It also has the notion of its own interface (a string describing
31     whether we run a registry, aggregate or slicemgr) and has
32     the notion of neighbour sfa services as defined
33     in /etc/sfa/{aggregates,registries}.xml
34
35     Finally it contains a cache instance
36
37     It gets augmented by the generic layer with
38     (*) an instance of manager (actually a manager module for now)
39         beware that this is shared among all instances of api
40     (*) an instance of a testbed driver
41     """
42
43     def __init__(self, encoding="utf-8", methods='sfa.methods',
44                  config="/etc/sfa/sfa_config",
45                  peer_cert=None, interface=None,
46                  key_file=None, cert_file=None, cache=None):
47
48         XmlrpcApi.__init__(self, encoding)
49
50         # we may be just be documenting the API
51         if config is None:
52             return
53         # Load configuration
54         self.config = Config(config)
55         self.credential = None
56         self.auth = Auth(peer_cert)
57         self.interface = interface
58         self.hrn = self.config.SFA_INTERFACE_HRN
59         self.key_file = key_file
60         self.key = Keypair(filename=self.key_file)
61         self.cert_file = cert_file
62         self.cert = Certificate(filename=self.cert_file)
63         self.cache = cache
64         if self.cache is None:
65             self.cache = Cache()
66
67         # load registries
68         from sfa.server.registry import Registries
69         self.registries = Registries()
70
71         # load aggregates
72         from sfa.server.aggregate import Aggregates
73         self.aggregates = Aggregates()
74
75         # filled later on by generic/Generic
76         self.manager = None
77         self._dbsession = None
78
79     def server_proxy(self, interface, cred, timeout=30):
80         """
81         Returns a connection to the specified interface. Use the specified
82         credential to determine the caller and look for the caller's key/cert
83         in the registry hierarchy cache.
84         """
85         from sfa.trust.hierarchy import Hierarchy
86         if not isinstance(cred, Credential):
87             cred_obj = Credential(string=cred)
88         else:
89             cred_obj = cred
90         caller_gid = cred_obj.get_gid_caller()
91         hierarchy = Hierarchy()
92         auth_info = hierarchy.get_auth_info(caller_gid.get_hrn())
93         key_file = auth_info.get_privkey_filename()
94         cert_file = auth_info.get_gid_filename()
95         server = interface.server_proxy(key_file, cert_file, timeout)
96         return server
97
98     def dbsession(self):
99         if self._dbsession is None:
100             self._dbsession = alchemy.session()
101         return self._dbsession
102
103     def close_dbsession(self):
104         if self._dbsession is None:
105             return
106         alchemy.close_session(self._dbsession)
107         self._dbsession = None
108
109     def getCredential(self, minimumExpiration=0):
110         """
111         Return a valid credential for this interface.
112         """
113         type = 'authority'
114         path = self.config.SFA_DATA_DIR
115         filename = ".".join([self.interface, self.hrn, type, "cred"])
116         cred_filename = os.path.join(path, filename)
117         cred = None
118         if os.path.isfile(cred_filename):
119             cred = Credential(filename=cred_filename)
120             # make sure cred isnt expired
121             if not cred.get_expiration or \
122                datetime.datetime.utcnow() + datetime.timedelta(seconds=minimumExpiration) < cred.get_expiration():
123                 return cred.save_to_string(save_parents=True)
124
125         # get a new credential
126         if self.interface in ['registry']:
127             cred = self._getCredentialRaw()
128         else:
129             cred = self._getCredential()
130         cred.save_to_file(cred_filename, save_parents=True)
131
132         return cred.save_to_string(save_parents=True)
133
134     def getDelegatedCredential(self, creds):
135         """
136         Attempt to find a credential delegated to us in
137         the specified list of creds.
138         """
139         from sfa.trust.hierarchy import Hierarchy
140         if creds and not isinstance(creds, list):
141             creds = [creds]
142         hierarchy = Hierarchy()
143
144         delegated_cred = None
145         for cred in creds:
146             if hierarchy.auth_exists(Credential(cred=cred).get_gid_caller().get_hrn()):
147                 delegated_cred = cred
148                 break
149         return delegated_cred
150
151     def _getCredential(self):
152         """
153         Get our credential from a remote registry
154         """
155         from sfa.server.registry import Registries
156         registries = Registries()
157         registry = registries.server_proxy(
158             self.hrn, self.key_file, self.cert_file)
159         cert_string = self.cert.save_to_string(save_parents=True)
160         # get self credential
161         self_cred = registry.GetSelfCredential(
162             cert_string, self.hrn, 'authority')
163         # get credential
164         cred = registry.GetCredential(self_cred, self.hrn, 'authority')
165         return Credential(string=cred)
166
167     def _getCredentialRaw(self):
168         """
169         Get our current credential directly from the local registry.
170         """
171
172         hrn = self.hrn
173         auth_hrn = self.auth.get_authority(hrn)
174
175         # is this a root or sub authority
176         if not auth_hrn or hrn == self.config.SFA_INTERFACE_HRN:
177             auth_hrn = hrn
178         auth_info = self.auth.get_auth_info(auth_hrn)
179         # xxx although unlikely we might want to check for a potential leak
180         dbsession = self.dbsession()
181         from sfa.storage.model import RegRecord
182         record = dbsession.query(RegRecord).filter_by(
183             type='authority+sa', hrn=hrn).first()
184         if not record:
185             raise RecordNotFound(hrn)
186         type = record.type
187         object_gid = record.get_gid_object()
188         new_cred = Credential(subject=object_gid.get_subject())
189         new_cred.set_gid_caller(object_gid)
190         new_cred.set_gid_object(object_gid)
191         new_cred.set_issuer_keys(
192             auth_info.get_privkey_filename(), auth_info.get_gid_filename())
193
194         r1 = determine_rights(type, hrn)
195         new_cred.set_privileges(r1)
196         new_cred.encode()
197         new_cred.sign()
198
199         return new_cred
200
201     def loadCredential(self):
202         """
203         Attempt to load credential from file if it exists. If it doesnt get
204         credential from registry.
205         """
206
207         # see if this file exists
208         # XX This is really the aggregate's credential. Using this is easier than getting
209         # the registry's credential from iteslf (ssl errors).
210         filename = self.interface + self.hrn + ".ma.cred"
211         ma_cred_path = os.path.join(self.config.SFA_DATA_DIR, filename)
212         try:
213             self.credential = Credential(filename=ma_cred_path)
214         except IOError:
215             self.credential = self.getCredentialFromRegistry()
216
217     def get_cached_server_version(self, server):
218         cache_key = server.url + "-version"
219         server_version = None
220         if self.cache:
221             server_version = self.cache.get(cache_key)
222         if not server_version:
223             result = server.GetVersion()
224             server_version = ReturnValue.get_value(result)
225             # cache version for 24 hours
226             self.cache.add(cache_key, server_version, ttl=60 * 60 * 24)
227         return server_version
228
229     def get_geni_code(self, result):
230         code = {
231             'geni_code': GENICODE.SUCCESS,
232             'am_type': 'sfa',
233             'am_code': None,
234         }
235         if isinstance(result, SfaFault):
236             code['geni_code'] = result.faultCode
237             code['am_code'] = result.faultCode
238
239         return code
240
241     def get_geni_value(self, result):
242         value = result
243         if isinstance(result, SfaFault):
244             value = ""
245         return value
246
247     def get_geni_output(self, result):
248         output = ""
249         if isinstance(result, SfaFault):
250             output = result.faultString
251         return output
252
253     def prepare_response_am(self, result):
254         version = version_core()
255         response = {
256             'geni_api': 3,
257             'code': self.get_geni_code(result),
258             'value': self.get_geni_value(result),
259             'output': self.get_geni_output(result),
260         }
261         return response
262
263     def prepare_response(self, result, method=""):
264         """
265         Converts the specified result into a standard GENI compliant
266         response
267         """
268         # as of dec 13 2011 we only support API v2
269         if self.interface.lower() in ['aggregate', 'slicemgr']:
270             result = self.prepare_response_am(result)
271         return XmlrpcApi.prepare_response(self, result, method)