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