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