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