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