Changes to registry manager. SA.
[sfa.git] / sfa / server / sfa-server.py
1 #!/usr/bin/python
2 #
3 # SFA PLC Wrapper
4 #
5 # This wrapper implements the SFA Registry and Slice Interfaces on PLC.
6 # Depending on command line options, it starts some combination of a
7 # Registry, an Aggregate Manager, and a Slice Manager.
8 #
9 # There are several items that need to be done before starting the wrapper
10 # server.
11 #
12 # NOTE:  Many configuration settings, including the PLC maintenance account
13 # credentials, URI of the PLCAPI, and PLC DB URI and admin credentials are initialized
14 # from your MyPLC configuration (/etc/planetlab/plc_config*).  Please make sure this information
15 # is up to date and accurate.
16 #
17 # 1) Import the existing planetlab database, creating the
18 #    appropriate SFA records. This is done by running the "sfa-import-plc.py" tool.
19 #
20 # 2) Create a "trusted_roots" directory and place the certificate of the root
21 #    authority in that directory. Given the defaults in sfa-import-plc.py, this
22 #    certificate would be named "planetlab.gid". For example,
23 #
24 #    mkdir trusted_roots; cp authorities/planetlab.gid trusted_roots/
25 #
26 # TODO: Can all three servers use the same "registry" certificate?
27 ##
28
29 # TCP ports for the three servers
30 #registry_port=12345
31 #aggregate_port=12346
32 #slicemgr_port=12347
33 ### xxx todo not in the config yet
34 component_port=12346
35 import os, os.path
36 import traceback
37 import sys
38 import sfa.util.xmlrpcprotocol as xmlrpcprotocol
39 from optparse import OptionParser
40
41 from sfa.util.sfalogging import logger
42 from sfa.trust.certificate import Keypair, Certificate
43 from sfa.trust.hierarchy import Hierarchy
44 from sfa.trust.gid import GID
45 from sfa.util.config import Config
46 from sfa.plc.api import SfaAPI
47 from sfa.server.registry import Registries
48 from sfa.server.aggregate import Aggregates
49 from sfa.util.xrn import get_authority, hrn_to_urn
50 from sfa.util.sfalogging import logger
51
52 # after http://www.erlenstar.demon.co.uk/unix/faq_2.html
53 def daemon():
54     """Daemonize the current process."""
55     if os.fork() != 0: os._exit(0)
56     os.setsid()
57     if os.fork() != 0: os._exit(0)
58     os.umask(0)
59     devnull = os.open(os.devnull, os.O_RDWR)
60     os.dup2(devnull, 0)
61     # xxx fixme - this is just to make sure that nothing gets stupidly lost - should use devnull
62     crashlog = os.open('/var/log/httpd/sfa_access_log', os.O_RDWR | os.O_APPEND | os.O_CREAT, 0644)
63     os.dup2(crashlog, 1)
64     os.dup2(crashlog, 2)
65
66 def init_server_key(server_key_file, server_cert_file, config, hierarchy):
67
68     hrn = config.SFA_INTERFACE_HRN.lower()
69     # check if the server's private key exists. If it doesnt,
70     # get the right one from the authorities directory. If it cant be
71     # found in the authorities directory, generate a random one
72     if not os.path.exists(server_key_file):
73         hrn = config.SFA_INTERFACE_HRN.lower()
74         hrn_parts = hrn.split(".")
75         rel_key_path = hrn
76         pkey_filename = hrn+".pkey"
77
78         # sub authority's have "." in their hrn. This must
79         # be converted to os.path separator
80         if len(hrn_parts) > 0:
81             rel_key_path = hrn.replace(".", os.sep)
82             pkey_filename= hrn_parts[-1]+".pkey"
83
84         key_file = os.sep.join([hierarchy.basedir, rel_key_path, pkey_filename])
85         if not os.path.exists(key_file):
86             # if it doesnt exist then this is probably a fresh interface
87             # with no records. Generate a random keypair for now
88             logger.debug("server's public key not found in %s" % key_file)
89
90             logger.debug("generating a random server key pair")
91             key = Keypair(create=True)
92             key.save_to_file(server_key_file)
93             init_server_cert(hrn, key, server_cert_file, self_signed=True)    
94
95         else:
96             # the pkey was found in the authorites directory. lets 
97             # copy it to where the server key should be and generate
98             # the cert
99             key = Keypair(filename=key_file)
100             key.save_to_file(server_key_file)
101             init_server_cert(hrn, key, server_cert_file)    
102
103     # If private key exists and cert doesnt, recreate cert
104     if (os.path.exists(server_key_file)) and (not os.path.exists(server_cert_file)):
105         key = Keypair(filename=server_key_file)
106         init_server_cert(hrn, key, server_cert_file)    
107
108
109 def init_server_cert(hrn, key, server_cert_file, self_signed=False):
110     """
111     Setup the certificate for this server. Attempt to use gid before 
112     creating a self signed cert 
113     """
114     if self_signed:
115         init_self_signed_cert(hrn, key, server_cert_file)
116     else:
117         try:
118             # look for gid file
119             logger.debug("generating server cert from gid: %s"% hrn)
120             hierarchy = Hierarchy()
121             auth_info = hierarchy.get_auth_info(hrn)
122             gid = GID(filename=auth_info.gid_filename)
123             gid.save_to_file(filename=server_cert_file)
124         except:
125             # fall back to self signed cert
126             logger.debug("gid for %s not found" % hrn)
127             init_self_signed_cert(hrn, key, server_cert_file)        
128         
129 def init_self_signed_cert(hrn, key, server_cert_file):
130     logger.debug("generating self signed cert")
131     # generate self signed certificate
132     cert = Certificate(subject=hrn)
133     cert.set_issuer(key=key, subject=hrn)
134     cert.set_pubkey(key)
135     cert.sign()
136     cert.save_to_file(server_cert_file)
137
138 def init_server(options, config):
139     """
140     Execute the init method defined in the manager file 
141     """
142     def init_manager(manager_module, manager_base):
143         try: manager = __import__(manager_module, fromlist=[manager_base])
144         except: manager = None
145         if manager and hasattr(manager, 'init_server'):
146             manager.init_server()
147     
148     manager_base = 'sfa.managers'
149     if options.registry:
150         mgr_type = config.SFA_REGISTRY_TYPE
151         manager_module = manager_base + ".registry_manager_%s" % mgr_type
152         init_manager(manager_module, manager_base)    
153     if options.am:
154         mgr_type = config.SFA_AGGREGATE_TYPE
155         manager_module = manager_base + ".aggregate_manager_%s" % mgr_type
156         init_manager(manager_module, manager_base)    
157     if options.sm:
158         mgr_type = config.SFA_SM_TYPE
159         manager_module = manager_base + ".slice_manager_%s" % mgr_type
160         init_manager(manager_module, manager_base)    
161     if options.cm:
162         mgr_type = config.SFA_CM_TYPE
163         manager_module = manager_base + ".component_manager_%s" % mgr_type
164         init_manager(manager_module, manager_base)    
165
166 def install_peer_certs(server_key_file, server_cert_file):
167     """
168     Attempt to install missing trusted gids and db records for 
169     our federated interfaces
170     """
171     # Attempt to get any missing peer gids
172     # There should be a gid file in /etc/sfa/trusted_roots for every
173     # peer registry found in in the registries.xml config file. If there
174     # are any missing gids, request a new one from the peer registry.
175     api = SfaAPI(key_file = server_key_file, cert_file = server_cert_file)
176     registries = Registries()
177     aggregates = Aggregates()
178     interfaces = dict(registries.items() + aggregates.items())
179     gids_current = api.auth.trusted_cert_list
180     hrns_current = [gid.get_hrn() for gid in gids_current]
181     hrns_expected = set([hrn for hrn in interfaces])
182     new_hrns = set(hrns_expected).difference(hrns_current)
183     #gids = self.get_peer_gids(new_hrns) + gids_current
184     peer_gids = []
185     if not new_hrns:
186         return 
187
188     trusted_certs_dir = api.config.get_trustedroots_dir()
189     for new_hrn in new_hrns:
190         if not new_hrn: continue
191         # the gid for this interface should already be installed
192         if new_hrn == api.config.SFA_INTERFACE_HRN: continue
193         try:
194             # get gid from the registry
195             url = interfaces[new_hrn].get_url()
196             interface = interfaces[new_hrn].get_server(server_key_file, server_cert_file, timeout=30)
197             # skip non sfa aggregates
198             server_version = api.get_cached_server_version(interface)
199             if 'sfa' not in server_version:
200                 logger.info("get_trusted_certs: skipping non sfa aggregate: %s" % new_hrn)
201                 continue
202       
203             trusted_gids = interface.get_trusted_certs()
204             if trusted_gids:
205                 # the gid we want should be the first one in the list,
206                 # but lets make sure
207                 for trusted_gid in trusted_gids:
208                     # default message
209                     message = "interface: %s\t" % (api.interface)
210                     message += "unable to install trusted gid for %s" % \
211                                (new_hrn)
212                     gid = GID(string=trusted_gids[0])
213                     peer_gids.append(gid)
214                     if gid.get_hrn() == new_hrn:
215                         gid_filename = os.path.join(trusted_certs_dir, '%s.gid' % new_hrn)
216                         gid.save_to_file(gid_filename, save_parents=True)
217                         message = "installed trusted cert for %s" % new_hrn
218                     # log the message
219                     api.logger.info(message)
220         except:
221             message = "interface: %s\tunable to install trusted gid for %s" % \
222                         (api.interface, new_hrn)
223             api.logger.log_exc(message)
224     # doesnt matter witch one
225     update_cert_records(peer_gids)
226
227 def update_cert_records(gids):
228     """
229     Make sure there is a record in the registry for the specified gids. 
230     Removes old records from the db.
231     """
232     # import SfaTable here so this module can be loaded by ComponentAPI
233     from sfa.util.table import SfaTable
234     from sfa.util.record import SfaRecord
235     if not gids:
236         return
237     table = SfaTable()
238     # get records that actually exist in the db
239     gid_urns = [gid.get_urn() for gid in gids]
240     hrns_expected = [gid.get_hrn() for gid in gids]
241     records_found = table.find({'hrn': hrns_expected, 'pointer': -1}) 
242
243     # remove old records
244     for record in records_found:
245         if record['hrn'] not in hrns_expected and \
246             record['hrn'] != self.api.config.SFA_INTERFACE_HRN:
247             table.remove(record)
248
249     # TODO: store urn in the db so we do this in 1 query 
250     for gid in gids:
251         hrn, type = gid.get_hrn(), gid.get_type()
252         record = table.find({'hrn': hrn, 'type': type, 'pointer': -1})
253         if not record:
254             record = {
255                 'hrn': hrn, 'type': type, 'pointer': -1,
256                 'authority': get_authority(hrn),
257                 'gid': gid.save_to_string(save_parents=True),
258             }
259             record = SfaRecord(dict=record)
260             table.insert(record)
261         
262 def main():
263     # Generate command line parser
264     parser = OptionParser(usage="sfa-server [options]")
265     parser.add_option("-r", "--registry", dest="registry", action="store_true",
266          help="run registry server", default=False)
267     parser.add_option("-s", "--slicemgr", dest="sm", action="store_true",
268          help="run slice manager", default=False)
269     parser.add_option("-a", "--aggregate", dest="am", action="store_true",
270          help="run aggregate manager", default=False)
271     parser.add_option("-c", "--component", dest="cm", action="store_true",
272          help="run component server", default=False)
273     parser.add_option("-t", "--trusted-certs", dest="trusted_certs", action="store_true",
274          help="refresh trusted certs", default=False)
275     parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
276          help="verbose mode - cumulative")
277     parser.add_option("-d", "--daemon", dest="daemon", action="store_true",
278          help="Run as daemon.", default=False)
279     (options, args) = parser.parse_args()
280     
281     config = Config()
282     if config.SFA_API_DEBUG: pass
283     hierarchy = Hierarchy()
284     server_key_file = os.path.join(hierarchy.basedir, "server.key")
285     server_cert_file = os.path.join(hierarchy.basedir, "server.cert")
286
287     init_server_key(server_key_file, server_cert_file, config, hierarchy)
288     init_server(options, config)
289  
290     if (options.daemon):  daemon()
291     
292     if options.trusted_certs:
293         install_peer_certs(server_key_file, server_cert_file)   
294     
295     # start registry server
296     if (options.registry):
297         from sfa.server.registry import Registry
298         r = Registry("", config.SFA_REGISTRY_PORT, server_key_file, server_cert_file)
299         r.start()
300
301     if (options.am):
302         from sfa.server.aggregate import Aggregate
303         a = Aggregate("", config.SFA_AGGREGATE_PORT, server_key_file, server_cert_file)
304         a.start()
305
306     # start slice manager
307     if (options.sm):
308         from sfa.server.slicemgr import SliceMgr
309         s = SliceMgr("", config.SFA_SM_PORT, server_key_file, server_cert_file)
310         s.start()
311
312     if (options.cm):
313         from sfa.server.component import Component
314         c = Component("", config.component_port, server_key_file, server_cert_file)
315 #        c = Component("", config.SFA_COMPONENT_PORT, server_key_file, server_cert_file)
316         c.start()
317
318 if __name__ == "__main__":
319     try:
320         main()
321     except:
322         logger.log_exc_critical("SFA server is exiting")