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