3 # PlanetLab SFA implementation
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.
9 # There are several items that need to be done before starting the servers.
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.
16 # 1) Import the existing planetlab database, creating the
17 # appropriate SFA records. This is done by running the "sfa-import-plc.py" tool.
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,
23 # mkdir trusted_roots; cp authorities/planetlab.gid trusted_roots/
25 # TODO: Can all three servers use the same "registry" certificate?
28 # TCP ports for the three servers
32 ### xxx todo not in the config yet
37 import sfa.util.xmlrpcprotocol as xmlrpcprotocol
38 from optparse import OptionParser
40 from sfa.util.sfalogging import logger
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.util.config import Config
45 from sfa.plc.plcsfaapi import PlcSfaApi
46 from sfa.server.registry import Registries
47 from sfa.server.aggregate import Aggregates
48 from sfa.util.xrn import get_authority, hrn_to_urn
49 from sfa.util.sfalogging import logger
51 # after http://www.erlenstar.demon.co.uk/unix/faq_2.html
53 """Daemonize the current process."""
54 if os.fork() != 0: os._exit(0)
56 if os.fork() != 0: os._exit(0)
58 devnull = os.open(os.devnull, os.O_RDWR)
60 # xxx fixme - this is just to make sure that nothing gets stupidly lost - should use devnull
61 crashlog = os.open('/var/log/httpd/sfa_access_log', os.O_RDWR | os.O_APPEND | os.O_CREAT, 0644)
65 def init_server_key(server_key_file, server_cert_file, config, hierarchy):
67 hrn = config.SFA_INTERFACE_HRN.lower()
68 # check if the server's private key exists. If it doesnt,
69 # get the right one from the authorities directory. If it cant be
70 # found in the authorities directory, generate a random one
71 if not os.path.exists(server_key_file):
72 hrn = config.SFA_INTERFACE_HRN.lower()
73 hrn_parts = hrn.split(".")
75 pkey_filename = hrn+".pkey"
77 # sub authority's have "." in their hrn. This must
78 # be converted to os.path separator
79 if len(hrn_parts) > 0:
80 rel_key_path = hrn.replace(".", os.sep)
81 pkey_filename= hrn_parts[-1]+".pkey"
83 key_file = os.sep.join([hierarchy.basedir, rel_key_path, pkey_filename])
84 if not os.path.exists(key_file):
85 # if it doesnt exist then this is probably a fresh interface
86 # with no records. Generate a random keypair for now
87 logger.debug("server's public key not found in %s" % key_file)
89 logger.debug("generating a random server key pair")
90 key = Keypair(create=True)
91 key.save_to_file(server_key_file)
92 init_server_cert(hrn, key, server_cert_file, self_signed=True)
95 # the pkey was found in the authorites directory. lets
96 # copy it to where the server key should be and generate
98 key = Keypair(filename=key_file)
99 key.save_to_file(server_key_file)
100 init_server_cert(hrn, key, server_cert_file)
102 # If private key exists and cert doesnt, recreate cert
103 if (os.path.exists(server_key_file)) and (not os.path.exists(server_cert_file)):
104 key = Keypair(filename=server_key_file)
105 init_server_cert(hrn, key, server_cert_file)
108 def init_server_cert(hrn, key, server_cert_file, self_signed=False):
110 Setup the certificate for this server. Attempt to use gid before
111 creating a self signed cert
114 init_self_signed_cert(hrn, key, server_cert_file)
118 logger.debug("generating server cert from gid: %s"% hrn)
119 hierarchy = Hierarchy()
120 auth_info = hierarchy.get_auth_info(hrn)
121 gid = GID(filename=auth_info.gid_filename)
122 gid.save_to_file(filename=server_cert_file)
124 # fall back to self signed cert
125 logger.debug("gid for %s not found" % hrn)
126 init_self_signed_cert(hrn, key, server_cert_file)
128 def init_self_signed_cert(hrn, key, server_cert_file):
129 logger.debug("generating self signed cert")
130 # generate self signed certificate
131 cert = Certificate(subject=hrn)
132 cert.set_issuer(key=key, subject=hrn)
135 cert.save_to_file(server_cert_file)
137 def install_peer_certs(server_key_file, server_cert_file):
139 Attempt to install missing trusted gids and db records for
140 our federated interfaces
142 # Attempt to get any missing peer gids
143 # There should be a gid file in /etc/sfa/trusted_roots for every
144 # peer registry found in in the registries.xml config file. If there
145 # are any missing gids, request a new one from the peer registry.
146 api = PlcSfaApi(key_file = server_key_file, cert_file = server_cert_file)
147 registries = Registries()
148 aggregates = Aggregates()
149 interfaces = dict(registries.items() + aggregates.items())
150 gids_current = api.auth.trusted_cert_list
151 hrns_current = [gid.get_hrn() for gid in gids_current]
152 hrns_expected = set([hrn for hrn in interfaces])
153 new_hrns = set(hrns_expected).difference(hrns_current)
154 #gids = self.get_peer_gids(new_hrns) + gids_current
159 trusted_certs_dir = api.config.get_trustedroots_dir()
160 for new_hrn in new_hrns:
161 if not new_hrn: continue
162 # the gid for this interface should already be installed
163 if new_hrn == api.config.SFA_INTERFACE_HRN: continue
165 # get gid from the registry
166 url = interfaces[new_hrn].get_url()
167 interface = interfaces[new_hrn].get_server(server_key_file, server_cert_file, timeout=30)
168 # skip non sfa aggregates
169 server_version = api.get_cached_server_version(interface)
170 if 'sfa' not in server_version:
171 logger.info("get_trusted_certs: skipping non sfa aggregate: %s" % new_hrn)
174 trusted_gids = interface.get_trusted_certs()
176 # the gid we want should be the first one in the list,
178 for trusted_gid in trusted_gids:
180 message = "interface: %s\t" % (api.interface)
181 message += "unable to install trusted gid for %s" % \
183 gid = GID(string=trusted_gids[0])
184 peer_gids.append(gid)
185 if gid.get_hrn() == new_hrn:
186 gid_filename = os.path.join(trusted_certs_dir, '%s.gid' % new_hrn)
187 gid.save_to_file(gid_filename, save_parents=True)
188 message = "installed trusted cert for %s" % new_hrn
190 api.logger.info(message)
192 message = "interface: %s\tunable to install trusted gid for %s" % \
193 (api.interface, new_hrn)
194 api.logger.log_exc(message)
195 # doesnt matter witch one
196 update_cert_records(peer_gids)
198 def update_cert_records(gids):
200 Make sure there is a record in the registry for the specified gids.
201 Removes old records from the db.
203 # import SfaTable here so this module can be loaded by PlcComponentApi
204 from sfa.util.table import SfaTable
205 from sfa.util.record import SfaRecord
209 # get records that actually exist in the db
210 gid_urns = [gid.get_urn() for gid in gids]
211 hrns_expected = [gid.get_hrn() for gid in gids]
212 records_found = table.find({'hrn': hrns_expected, 'pointer': -1})
215 for record in records_found:
216 if record['hrn'] not in hrns_expected and \
217 record['hrn'] != self.api.config.SFA_INTERFACE_HRN:
220 # TODO: store urn in the db so we do this in 1 query
222 hrn, type = gid.get_hrn(), gid.get_type()
223 record = table.find({'hrn': hrn, 'type': type, 'pointer': -1})
226 'hrn': hrn, 'type': type, 'pointer': -1,
227 'authority': get_authority(hrn),
228 'gid': gid.save_to_string(save_parents=True),
230 record = SfaRecord(dict=record)
234 # Generate command line parser
235 parser = OptionParser(usage="sfa-start.py [options]")
236 parser.add_option("-r", "--registry", dest="registry", action="store_true",
237 help="run registry server", default=False)
238 parser.add_option("-s", "--slicemgr", dest="sm", action="store_true",
239 help="run slice manager", default=False)
240 parser.add_option("-a", "--aggregate", dest="am", action="store_true",
241 help="run aggregate manager", default=False)
242 parser.add_option("-c", "--component", dest="cm", action="store_true",
243 help="run component server", default=False)
244 parser.add_option("-t", "--trusted-certs", dest="trusted_certs", action="store_true",
245 help="refresh trusted certs", default=False)
246 parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0,
247 help="verbose mode - cumulative")
248 parser.add_option("-d", "--daemon", dest="daemon", action="store_true",
249 help="Run as daemon.", default=False)
250 (options, args) = parser.parse_args()
253 if config.SFA_API_DEBUG: pass
254 hierarchy = Hierarchy()
255 server_key_file = os.path.join(hierarchy.basedir, "server.key")
256 server_cert_file = os.path.join(hierarchy.basedir, "server.cert")
258 init_server_key(server_key_file, server_cert_file, config, hierarchy)
260 if (options.daemon): daemon()
262 if options.trusted_certs:
263 install_peer_certs(server_key_file, server_cert_file)
265 # start registry server
266 if (options.registry):
267 from sfa.server.registry import Registry
268 r = Registry("", config.SFA_REGISTRY_PORT, server_key_file, server_cert_file)
272 from sfa.server.aggregate import Aggregate
273 a = Aggregate("", config.SFA_AGGREGATE_PORT, server_key_file, server_cert_file)
276 # start slice manager
278 from sfa.server.slicemgr import SliceMgr
279 s = SliceMgr("", config.SFA_SM_PORT, server_key_file, server_cert_file)
283 from sfa.server.component import Component
284 c = Component("", config.component_port, server_key_file, server_cert_file)
285 # c = Component("", config.SFA_COMPONENT_PORT, server_key_file, server_cert_file)
288 if __name__ == "__main__":
292 logger.log_exc_critical("SFA server is exiting")