0b999d9de902eb9755524fc4e4a266c89a646980
[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.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 ### xxx todo not in the config yet
29 component_port=12346
30 import os, os.path
31 import traceback
32 import sys
33 from optparse import OptionParser
34
35 from sfa.util.sfalogging import logger
36 from sfa.util.xrn import get_authority, hrn_to_urn
37 from sfa.util.config import Config
38 from sfa.trust.gid import GID
39 from sfa.trust.trustedroots import TrustedRoots
40 from sfa.trust.certificate import Keypair, Certificate
41 from sfa.trust.hierarchy import Hierarchy
42 from sfa.trust.gid import GID
43 from sfa.server.sfaapi import SfaApi
44 from sfa.server.registry import Registries
45 from sfa.server.aggregate import Aggregates
46 from sfa.client.return_value import ReturnValue
47
48 # after http://www.erlenstar.demon.co.uk/unix/faq_2.html
49 def daemon():
50     """Daemonize the current process."""
51     if os.fork() != 0: os._exit(0)
52     os.setsid()
53     if os.fork() != 0: os._exit(0)
54     os.umask(0)
55     devnull = os.open(os.devnull, os.O_RDWR)
56     os.dup2(devnull, 0)
57     # xxx fixme - this is just to make sure that nothing gets stupidly lost - should use devnull
58     logdir='/var/log/httpd'
59     # when installed in standalone we might not have httpd installed
60     if not os.path.isdir(logdir): os.mkdir('/var/log/httpd')
61     crashlog = os.open('%s/sfa_access_log'%logdir, os.O_RDWR | os.O_APPEND | os.O_CREAT, 0644)
62     os.dup2(crashlog, 1)
63     os.dup2(crashlog, 2)
64
65
66 def install_peer_certs(server_key_file, server_cert_file):
67     """
68     Attempt to install missing trusted gids and db records for 
69     our federated interfaces
70     """
71     # Attempt to get any missing peer gids
72     # There should be a gid file in /etc/sfa/trusted_roots for every
73     # peer registry found in in the registries.xml config file. If there
74     # are any missing gids, request a new one from the peer registry.
75     api = SfaApi(key_file = server_key_file, cert_file = server_cert_file)
76     registries = Registries()
77     aggregates = Aggregates()
78     interfaces = dict(registries.items() + aggregates.items())
79     gids_current = api.auth.trusted_cert_list
80     hrns_current = [gid.get_hrn() for gid in gids_current]
81     hrns_expected = set([hrn for hrn in interfaces])
82     new_hrns = set(hrns_expected).difference(hrns_current)
83     #gids = self.get_peer_gids(new_hrns) + gids_current
84     peer_gids = []
85     if not new_hrns:
86         return 
87     trusted_certs_dir = api.config.get_trustedroots_dir()
88     for new_hrn in new_hrns: 
89         if not new_hrn: continue
90         # the gid for this interface should already be installed
91         if new_hrn == api.config.SFA_INTERFACE_HRN: continue
92         try:
93             # get gid from the registry
94             url = interfaces[new_hrn].get_url()
95             interface = interfaces[new_hrn].server_proxy(server_key_file, server_cert_file, timeout=30)
96             # skip non sfa aggregates
97             server_version = api.get_cached_server_version(interface)
98             if 'sfa' not in server_version:
99                 logger.info("get_trusted_certs: skipping non sfa aggregate: %s" % new_hrn)
100                 continue
101             trusted_gids = ReturnValue.get_value(interface.get_trusted_certs())
102             if trusted_gids:
103                 # the gid we want should be the first one in the list,
104                 # but lets make sure
105                 for trusted_gid in trusted_gids:
106                     # default message
107                     message = "interface: %s\t" % (api.interface)
108                     message += "unable to install trusted gid for %s" % \
109                                (new_hrn)
110                     gid = GID(string=trusted_gid)
111                     peer_gids.append(gid)
112                     if gid.get_hrn() == new_hrn:
113                         gid_filename = os.path.join(trusted_certs_dir, '%s.gid' % new_hrn)
114                         gid.save_to_file(gid_filename, save_parents=True)
115                         message = "installed trusted cert for %s" % new_hrn
116                     # log the message
117                     api.logger.info(message)
118         except:
119             message = "interface: %s\tunable to install trusted gid for %s" % \
120                         (api.interface, new_hrn)
121             api.logger.log_exc(message)
122     # doesnt matter witch one
123     update_cert_records(peer_gids)
124
125 def update_cert_records(gids):
126     """
127     Make sure there is a record in the registry for the specified gids. 
128     Removes old records from the db.
129     """
130     # import db stuff here here so this module can be loaded by PlcComponentApi
131     from sfa.storage.alchemy import dbsession
132     from sfa.storage.model import RegRecord
133     if not gids:
134         return
135     # get records that actually exist in the db
136     gid_urns = [gid.get_urn() for gid in gids]
137     hrns_expected = [gid.get_hrn() for gid in gids]
138     records_found = dbsession.query(RegRecord).\
139         filter_by(pointer=-1).filter(RegRecord.hrn.in_(hrns_expected)).all()
140
141     # remove old records
142     for record in records_found:
143         if record.hrn not in hrns_expected and \
144             record.hrn != self.api.config.SFA_INTERFACE_HRN:
145             dbsession.delete(record)
146
147     # TODO: store urn in the db so we do this in 1 query 
148     for gid in gids:
149         hrn, type = gid.get_hrn(), gid.get_type()
150         record = dbsession.query(RegRecord).filter_by(hrn=hrn, type=type,pointer=-1).first()
151         if not record:
152             record = RegRecord (dict= {'type':type,
153                                        'hrn': hrn, 
154                                        'authority': get_authority(hrn),
155                                        'gid': gid.save_to_string(save_parents=True),
156                                        })
157             dbsession.add(record)
158     dbsession.commit()
159         
160 def main():
161     # Generate command line parser
162     parser = OptionParser(usage="sfa-start.py [options]")
163     parser.add_option("-r", "--registry", dest="registry", action="store_true",
164          help="run registry server", default=False)
165     parser.add_option("-s", "--slicemgr", dest="sm", action="store_true",
166          help="run slice manager", default=False)
167     parser.add_option("-a", "--aggregate", dest="am", action="store_true",
168          help="run aggregate manager", default=False)
169     parser.add_option("-c", "--component", dest="cm", action="store_true",
170          help="run component server", default=False)
171     parser.add_option("-t", "--trusted-certs", dest="trusted_certs", action="store_true",
172          help="refresh trusted certs", default=False)
173     parser.add_option("-d", "--daemon", dest="daemon", action="store_true",
174          help="Run as daemon.", default=False)
175     (options, args) = parser.parse_args()
176     
177     config = Config()
178     logger.setLevelFromOptVerbose(config.SFA_API_LOGLEVEL)
179     
180
181     # ge the server's key and cert
182     hierarchy = Hierarchy()
183     auth_info = hierarchy.get_interface_auth_info()
184     server_key_file = auth_info.get_privkey_filename()
185     server_cert_file = auth_info.get_gid_filename() 
186     # ensure interface cert is present in trusted roots dir
187     trusted_roots = TrustedRoots(config.get_trustedroots_dir())
188     trusted_roots.add_gid(GID(filename=server_cert_file))
189     if (options.daemon):  daemon()
190     
191     if options.trusted_certs:
192         install_peer_certs(server_key_file, server_cert_file)   
193     
194     # start registry server
195     if (options.registry):
196         from sfa.server.registry import Registry
197         r = Registry("", config.SFA_REGISTRY_PORT, server_key_file, server_cert_file)
198         r.start()
199
200     if (options.am):
201         from sfa.server.aggregate import Aggregate
202         a = Aggregate("", config.SFA_AGGREGATE_PORT, server_key_file, server_cert_file)
203         a.start()
204
205     # start slice manager
206     if (options.sm):
207         from sfa.server.slicemgr import SliceMgr
208         s = SliceMgr("", config.SFA_SM_PORT, server_key_file, server_cert_file)
209         s.start()
210
211     if (options.cm):
212         from sfa.server.component import Component
213         c = Component("", config.component_port, server_key_file, server_cert_file)
214 #        c = Component("", config.SFA_COMPONENT_PORT, server_key_file, server_cert_file)
215         c.start()
216
217 if __name__ == "__main__":
218     try:
219         main()
220     except:
221         logger.log_exc_critical("SFA server is exiting")