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