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