rename xmlrpcprotocol into sfaprotocol
[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 from sfa.trust.gid import GID
43 from sfa.trust.trustedroots import TrustedRoots
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     logdir='/var/log/httpd'
64     # when installed in standalone we might not have httpd installed
65     if not os.path.isdir(logdir): os.mkdir('/var/log/httpd')
66     crashlog = os.open('%s/sfa_access_log'%logdir, os.O_RDWR | os.O_APPEND | os.O_CREAT, 0644)
67     os.dup2(crashlog, 1)
68     os.dup2(crashlog, 2)
69
70
71 def install_peer_certs(server_key_file, server_cert_file):
72     """
73     Attempt to install missing trusted gids and db records for 
74     our federated interfaces
75     """
76     # Attempt to get any missing peer gids
77     # There should be a gid file in /etc/sfa/trusted_roots for every
78     # peer registry found in in the registries.xml config file. If there
79     # are any missing gids, request a new one from the peer registry.
80     api = SfaApi(key_file = server_key_file, cert_file = server_cert_file)
81     registries = Registries()
82     aggregates = Aggregates()
83     interfaces = dict(registries.items() + aggregates.items())
84     gids_current = api.auth.trusted_cert_list
85     hrns_current = [gid.get_hrn() for gid in gids_current]
86     hrns_expected = set([hrn for hrn in interfaces])
87     new_hrns = set(hrns_expected).difference(hrns_current)
88     #gids = self.get_peer_gids(new_hrns) + gids_current
89     peer_gids = []
90     if not new_hrns:
91         return 
92
93     trusted_certs_dir = api.config.get_trustedroots_dir()
94     for new_hrn in new_hrns:
95         if not new_hrn: continue
96         # the gid for this interface should already be installed
97         if new_hrn == api.config.SFA_INTERFACE_HRN: continue
98         try:
99             # get gid from the registry
100             url = interfaces[new_hrn].get_url()
101             interface = interfaces[new_hrn].server_proxy(server_key_file, server_cert_file, timeout=30)
102             # skip non sfa aggregates
103             server_version = api.get_cached_server_version(interface)
104             if 'sfa' not in server_version:
105                 logger.info("get_trusted_certs: skipping non sfa aggregate: %s" % new_hrn)
106                 continue
107       
108             trusted_gids = interface.get_trusted_certs()
109             if trusted_gids:
110                 # the gid we want should be the first one in the list,
111                 # but lets make sure
112                 for trusted_gid in trusted_gids:
113                     # default message
114                     message = "interface: %s\t" % (api.interface)
115                     message += "unable to install trusted gid for %s" % \
116                                (new_hrn)
117                     gid = GID(string=trusted_gids[0])
118                     peer_gids.append(gid)
119                     if gid.get_hrn() == new_hrn:
120                         gid_filename = os.path.join(trusted_certs_dir, '%s.gid' % new_hrn)
121                         gid.save_to_file(gid_filename, save_parents=True)
122                         message = "installed trusted cert for %s" % new_hrn
123                     # log the message
124                     api.logger.info(message)
125         except:
126             message = "interface: %s\tunable to install trusted gid for %s" % \
127                         (api.interface, new_hrn)
128             api.logger.log_exc(message)
129     # doesnt matter witch one
130     update_cert_records(peer_gids)
131
132 def update_cert_records(gids):
133     """
134     Make sure there is a record in the registry for the specified gids. 
135     Removes old records from the db.
136     """
137     # import SfaTable here so this module can be loaded by PlcComponentApi
138     from sfa.storage.table import SfaTable
139     from sfa.storage.record import SfaRecord
140     if not gids:
141         return
142     table = SfaTable()
143     # get records that actually exist in the db
144     gid_urns = [gid.get_urn() for gid in gids]
145     hrns_expected = [gid.get_hrn() for gid in gids]
146     records_found = table.find({'hrn': hrns_expected, 'pointer': -1}) 
147
148     # remove old records
149     for record in records_found:
150         if record['hrn'] not in hrns_expected and \
151             record['hrn'] != self.api.config.SFA_INTERFACE_HRN:
152             table.remove(record)
153
154     # TODO: store urn in the db so we do this in 1 query 
155     for gid in gids:
156         hrn, type = gid.get_hrn(), gid.get_type()
157         record = table.find({'hrn': hrn, 'type': type, 'pointer': -1})
158         if not record:
159             record = {
160                 'hrn': hrn, 'type': type, 'pointer': -1,
161                 'authority': get_authority(hrn),
162                 'gid': gid.save_to_string(save_parents=True),
163             }
164             record = SfaRecord(dict=record)
165             table.insert(record)
166         
167 def main():
168     # Generate command line parser
169     parser = OptionParser(usage="sfa-start.py [options]")
170     parser.add_option("-r", "--registry", dest="registry", action="store_true",
171          help="run registry server", default=False)
172     parser.add_option("-s", "--slicemgr", dest="sm", action="store_true",
173          help="run slice manager", default=False)
174     parser.add_option("-a", "--aggregate", dest="am", action="store_true",
175          help="run aggregate manager", default=False)
176     parser.add_option("-c", "--component", dest="cm", action="store_true",
177          help="run component server", default=False)
178     parser.add_option("-t", "--trusted-certs", dest="trusted_certs", action="store_true",
179          help="refresh trusted certs", default=False)
180     parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
181          help="verbose mode - cumulative")
182     parser.add_option("-d", "--daemon", dest="daemon", action="store_true",
183          help="Run as daemon.", default=False)
184     (options, args) = parser.parse_args()
185     
186     config = Config()
187     if config.SFA_API_DEBUG: pass
188
189     # ge the server's key and cert
190     hierarchy = Hierarchy()
191     auth_info = hierarchy.get_interface_auth_info()
192     server_key_file = auth_info.get_privkey_filename()
193     server_cert_file = auth_info.get_gid_filename()
194
195     # ensure interface cert is present in trusted roots dir
196     trusted_roots = TrustedRoots(config.get_trustedroots_dir())
197     trusted_roots.add_gid(GID(filename=server_cert_file))
198     if (options.daemon):  daemon()
199     
200     if options.trusted_certs:
201         install_peer_certs(server_key_file, server_cert_file)   
202     
203     # start registry server
204     if (options.registry):
205         from sfa.server.registry import Registry
206         r = Registry("", config.SFA_REGISTRY_PORT, server_key_file, server_cert_file)
207         r.start()
208
209     if (options.am):
210         from sfa.server.aggregate import Aggregate
211         a = Aggregate("", config.SFA_AGGREGATE_PORT, server_key_file, server_cert_file)
212         a.start()
213
214     # start slice manager
215     if (options.sm):
216         from sfa.server.slicemgr import SliceMgr
217         s = SliceMgr("", config.SFA_SM_PORT, server_key_file, server_cert_file)
218         s.start()
219
220     if (options.cm):
221         from sfa.server.component import Component
222         c = Component("", config.component_port, server_key_file, server_cert_file)
223 #        c = Component("", config.SFA_COMPONENT_PORT, server_key_file, server_cert_file)
224         c.start()
225
226 if __name__ == "__main__":
227     try:
228         main()
229     except:
230         logger.log_exc_critical("SFA server is exiting")