X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=sfa%2Fserver%2Fsfa-server.py;h=fadb1d3d74291afbcf2f883880800b0d4faee751;hb=0f57e9a45ec399ae3157f48aa7db7921706315f3;hp=4614e7450b54cb0e9dc0c171e347af2bfd62d6d9;hpb=715c2ed6a182d8b95feeb83c91b41a0407feadb1;p=sfa.git diff --git a/sfa/server/sfa-server.py b/sfa/server/sfa-server.py index 4614e745..fadb1d3d 100755 --- a/sfa/server/sfa-server.py +++ b/sfa/server/sfa-server.py @@ -1,16 +1,12 @@ #!/usr/bin/python # -### $Id$ -### $URL$ +# PlanetLab SFA implementation # -# GENI PLC Wrapper -# -# This wrapper implements the Geni Registry and Slice Interfaces on PLC. +# This implements the SFA Registry and Slice Interfaces on PLC. # Depending on command line options, it starts some combination of a # Registry, an Aggregate Manager, and a Slice Manager. # -# There are several items that need to be done before starting the wrapper -# server. +# There are several items that need to be done before starting the servers. # # NOTE: Many configuration settings, including the PLC maintenance account # credentials, URI of the PLCAPI, and PLC DB URI and admin credentials are initialized @@ -18,7 +14,7 @@ # is up to date and accurate. # # 1) Import the existing planetlab database, creating the -# appropriate geni records. This is done by running the "sfa-import-plc.py" tool. +# appropriate SFA records. This is done by running the "sfa-import-plc.py" tool. # # 2) Create a "trusted_roots" directory and place the certificate of the root # authority in that directory. Given the defaults in sfa-import-plc.py, this @@ -30,23 +26,29 @@ ## # TCP ports for the three servers -registry_port=12345 -aggregate_port=12346 -slicemgr_port=12347 - +#registry_port=12345 +#aggregate_port=12346 +#slicemgr_port=12347 +### xxx todo not in the config yet +component_port=12346 import os, os.path +import traceback import sys +import sfa.util.xmlrpcprotocol as xmlrpcprotocol from optparse import OptionParser -from sfa.trust.trustedroot import TrustedRootList +from sfa.util.sfalogging import logger from sfa.trust.certificate import Keypair, Certificate - -from sfa.server.registry import Registry -from sfa.server.aggregate import Aggregate -from sfa.server.slicemgr import SliceMgr from sfa.trust.hierarchy import Hierarchy +from sfa.trust.gid import GID from sfa.util.config import Config -from sfa.util.report import trace +from sfa.plc.api import SfaAPI +from sfa.server.registry import Registries +from sfa.server.aggregate import Aggregates +from sfa.util.xrn import get_authority, hrn_to_urn +from sfa.util.sfalogging import logger + +from sfa.managers.import_manager import import_manager # after http://www.erlenstar.demon.co.uk/unix/faq_2.html def daemon(): @@ -58,59 +60,38 @@ def daemon(): devnull = os.open(os.devnull, os.O_RDWR) os.dup2(devnull, 0) # xxx fixme - this is just to make sure that nothing gets stupidly lost - should use devnull - crashlog = os.open('/var/log/sfa.daemon', os.O_RDWR | os.O_APPEND | os.O_CREAT, 0644) + crashlog = os.open('/var/log/httpd/sfa_access_log', os.O_RDWR | os.O_APPEND | os.O_CREAT, 0644) os.dup2(crashlog, 1) os.dup2(crashlog, 2) -def main(): - # xxx get rid of globals - name consistently CamelCase or under_score - global AuthHierarchy - global TrustedRoots - global registry_port - global aggregate_port - global slicemgr_port - - # Generate command line parser - parser = OptionParser(usage="sfa-server [options]") - parser.add_option("-r", "--registry", dest="registry", action="store_true", - help="run registry server", default=False) - parser.add_option("-s", "--slicemgr", dest="sm", action="store_true", - help="run slice manager", default=False) - parser.add_option("-a", "--aggregate", dest="am", action="store_true", - help="run aggregate manager", default=False) - parser.add_option("-v", "--verbose", dest="verbose", action="store_true", - help="verbose mode", default=False) - parser.add_option("-d", "--daemon", dest="daemon", action="store_true", - help="Run as daemon.", default=False) - (options, args) = parser.parse_args() +def init_server_key(server_key_file, server_cert_file, config, hierarchy): - config = Config() - hierarchy = Hierarchy() - trusted_roots = TrustedRootList(config.get_trustedroots_dir()) - server_key_file = os.path.join(hierarchy.basedir, "server.key") - server_cert_file = os.path.join(hierarchy.basedir, "server.cert") - # XX TODO: Subject should be the interfaces's hrn - subject = "registry" - if (options.daemon): daemon() - + hrn = config.SFA_INTERFACE_HRN.lower() # check if the server's private key exists. If it doesnt, # get the right one from the authorities directory. If it cant be # found in the authorities directory, generate a random one if not os.path.exists(server_key_file): hrn = config.SFA_INTERFACE_HRN.lower() - key_file = os.sep.join([hierarchy.basedir, hrn, hrn+".pkey"]) + hrn_parts = hrn.split(".") + rel_key_path = hrn + pkey_filename = hrn+".pkey" + + # sub authority's have "." in their hrn. This must + # be converted to os.path separator + if len(hrn_parts) > 0: + rel_key_path = hrn.replace(".", os.sep) + pkey_filename= hrn_parts[-1]+".pkey" + + key_file = os.sep.join([hierarchy.basedir, rel_key_path, pkey_filename]) if not os.path.exists(key_file): # if it doesnt exist then this is probably a fresh interface # with no records. Generate a random keypair for now - trace("server's public key not found in %s" % key_file) - trace("generating a random server key pair") + logger.debug("server's public key not found in %s" % key_file) + + logger.debug("generating a random server key pair") key = Keypair(create=True) key.save_to_file(server_key_file) - cert = Certificate(subject=subject) - cert.set_issuer(key=key, subject=subject) - cert.set_pubkey(key) - cert.sign() - cert.save_to_file(server_cert_file) + init_server_cert(hrn, key, server_cert_file, self_signed=True) else: # the pkey was found in the authorites directory. lets @@ -118,36 +99,219 @@ def main(): # the cert key = Keypair(filename=key_file) key.save_to_file(server_key_file) - cert = Certificate(subject=subject) - cert.set_issuer(key=key, subject=subject) - cert.set_pubkey(key) - cert.sign() - cert.save_to_file(server_cert_file) - + init_server_cert(hrn, key, server_cert_file) # If private key exists and cert doesnt, recreate cert if (os.path.exists(server_key_file)) and (not os.path.exists(server_cert_file)): key = Keypair(filename=server_key_file) - cert = Certificate(subject=subject) - cert.set_issuer(key=key, subject=subject) - cert.set_pubkey(key) - cert.sign() - cert.save_to_file(server_cert_file) + init_server_cert(hrn, key, server_cert_file) + + +def init_server_cert(hrn, key, server_cert_file, self_signed=False): + """ + Setup the certificate for this server. Attempt to use gid before + creating a self signed cert + """ + if self_signed: + init_self_signed_cert(hrn, key, server_cert_file) + else: + try: + # look for gid file + logger.debug("generating server cert from gid: %s"% hrn) + hierarchy = Hierarchy() + auth_info = hierarchy.get_auth_info(hrn) + gid = GID(filename=auth_info.gid_filename) + gid.save_to_file(filename=server_cert_file) + except: + # fall back to self signed cert + logger.debug("gid for %s not found" % hrn) + init_self_signed_cert(hrn, key, server_cert_file) + +def init_self_signed_cert(hrn, key, server_cert_file): + logger.debug("generating self signed cert") + # generate self signed certificate + cert = Certificate(subject=hrn) + cert.set_issuer(key=key, subject=hrn) + cert.set_pubkey(key) + cert.sign() + cert.save_to_file(server_cert_file) +def init_server(options, config): + """ + Locate the manager based on config.*TYPE + Execute the init_server method (well in fact function, sigh) if defined in that module + In order to migrate to a more generic approach: + * search for <>_manager_.py + * if not found, try <>_manager.py (and issue a warning if !='pl') + """ + if options.registry: + manager=import_manager ("registry", config.SFA_REGISTRY_TYPE) + if manager and hasattr(manager, 'init_server'): manager.init_server() + if options.am: + manager=import_manager ("aggregate", config.SFA_AGGREGATE_TYPE) + if manager and hasattr(manager, 'init_server'): manager.init_server() + if options.sm: + manager=import_manager ("slice", config.SFA_SM_TYPE) + if manager and hasattr(manager, 'init_server'): manager.init_server() + if options.cm: + manager=import_manager ("component", config.SFA_CM_TYPE) + if manager and hasattr(manager, 'init_server'): manager.init_server() + + +def install_peer_certs(server_key_file, server_cert_file): + """ + Attempt to install missing trusted gids and db records for + our federated interfaces + """ + # Attempt to get any missing peer gids + # There should be a gid file in /etc/sfa/trusted_roots for every + # peer registry found in in the registries.xml config file. If there + # are any missing gids, request a new one from the peer registry. + api = SfaAPI(key_file = server_key_file, cert_file = server_cert_file) + registries = Registries() + aggregates = Aggregates() + interfaces = dict(registries.items() + aggregates.items()) + gids_current = api.auth.trusted_cert_list + hrns_current = [gid.get_hrn() for gid in gids_current] + hrns_expected = set([hrn for hrn in interfaces]) + new_hrns = set(hrns_expected).difference(hrns_current) + #gids = self.get_peer_gids(new_hrns) + gids_current + peer_gids = [] + if not new_hrns: + return + + trusted_certs_dir = api.config.get_trustedroots_dir() + for new_hrn in new_hrns: + if not new_hrn: continue + # the gid for this interface should already be installed + if new_hrn == api.config.SFA_INTERFACE_HRN: continue + try: + # get gid from the registry + url = interfaces[new_hrn].get_url() + interface = interfaces[new_hrn].get_server(server_key_file, server_cert_file, timeout=30) + # skip non sfa aggregates + server_version = api.get_cached_server_version(interface) + if 'sfa' not in server_version: + logger.info("get_trusted_certs: skipping non sfa aggregate: %s" % new_hrn) + continue + + trusted_gids = interface.get_trusted_certs() + if trusted_gids: + # the gid we want should be the first one in the list, + # but lets make sure + for trusted_gid in trusted_gids: + # default message + message = "interface: %s\t" % (api.interface) + message += "unable to install trusted gid for %s" % \ + (new_hrn) + gid = GID(string=trusted_gids[0]) + peer_gids.append(gid) + if gid.get_hrn() == new_hrn: + gid_filename = os.path.join(trusted_certs_dir, '%s.gid' % new_hrn) + gid.save_to_file(gid_filename, save_parents=True) + message = "installed trusted cert for %s" % new_hrn + # log the message + api.logger.info(message) + except: + message = "interface: %s\tunable to install trusted gid for %s" % \ + (api.interface, new_hrn) + api.logger.log_exc(message) + # doesnt matter witch one + update_cert_records(peer_gids) + +def update_cert_records(gids): + """ + Make sure there is a record in the registry for the specified gids. + Removes old records from the db. + """ + # import SfaTable here so this module can be loaded by ComponentAPI + from sfa.util.table import SfaTable + from sfa.util.record import SfaRecord + if not gids: + return + table = SfaTable() + # get records that actually exist in the db + gid_urns = [gid.get_urn() for gid in gids] + hrns_expected = [gid.get_hrn() for gid in gids] + records_found = table.find({'hrn': hrns_expected, 'pointer': -1}) + + # remove old records + for record in records_found: + if record['hrn'] not in hrns_expected and \ + record['hrn'] != self.api.config.SFA_INTERFACE_HRN: + table.remove(record) + + # TODO: store urn in the db so we do this in 1 query + for gid in gids: + hrn, type = gid.get_hrn(), gid.get_type() + record = table.find({'hrn': hrn, 'type': type, 'pointer': -1}) + if not record: + record = { + 'hrn': hrn, 'type': type, 'pointer': -1, + 'authority': get_authority(hrn), + 'gid': gid.save_to_string(save_parents=True), + } + record = SfaRecord(dict=record) + table.insert(record) + +def main(): + # Generate command line parser + parser = OptionParser(usage="sfa-server [options]") + parser.add_option("-r", "--registry", dest="registry", action="store_true", + help="run registry server", default=False) + parser.add_option("-s", "--slicemgr", dest="sm", action="store_true", + help="run slice manager", default=False) + parser.add_option("-a", "--aggregate", dest="am", action="store_true", + help="run aggregate manager", default=False) + parser.add_option("-c", "--component", dest="cm", action="store_true", + help="run component server", default=False) + parser.add_option("-t", "--trusted-certs", dest="trusted_certs", action="store_true", + help="refresh trusted certs", default=False) + parser.add_option("-v", "--verbose", action="count", dest="verbose", default=0, + help="verbose mode - cumulative") + parser.add_option("-d", "--daemon", dest="daemon", action="store_true", + help="Run as daemon.", default=False) + (options, args) = parser.parse_args() + + config = Config() + if config.SFA_API_DEBUG: pass + hierarchy = Hierarchy() + server_key_file = os.path.join(hierarchy.basedir, "server.key") + server_cert_file = os.path.join(hierarchy.basedir, "server.cert") + + init_server_key(server_key_file, server_cert_file, config, hierarchy) + init_server(options, config) + + if (options.daemon): daemon() + + if options.trusted_certs: + install_peer_certs(server_key_file, server_cert_file) + # start registry server if (options.registry): - r = Registry("", registry_port, server_key_file, server_cert_file) + from sfa.server.registry import Registry + r = Registry("", config.SFA_REGISTRY_PORT, server_key_file, server_cert_file) r.start() - # start aggregate manager if (options.am): - a = Aggregate("", aggregate_port, server_key_file, server_cert_file) + from sfa.server.aggregate import Aggregate + a = Aggregate("", config.SFA_AGGREGATE_PORT, server_key_file, server_cert_file) a.start() # start slice manager if (options.sm): - s = SliceMgr("", slicemgr_port, server_key_file, server_cert_file) + from sfa.server.slicemgr import SliceMgr + s = SliceMgr("", config.SFA_SM_PORT, server_key_file, server_cert_file) s.start() + if (options.cm): + from sfa.server.component import Component + c = Component("", config.component_port, server_key_file, server_cert_file) +# c = Component("", config.SFA_COMPONENT_PORT, server_key_file, server_cert_file) + c.start() + if __name__ == "__main__": - main() + try: + main() + except: + logger.log_exc_critical("SFA server is exiting")