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