#!/usr/bin/env python3 """ core-daemon: the CORE daemon is a server process that receives CORE API messages and instantiates emulated nodes and networks within the kernel. Various message handlers are defined and some support for sending messages. """ import argparse import logging import os import sys import threading import time from configparser import ConfigParser from core import constants from core.api.grpc.server import CoreGrpcServer from core.api.tlv.corehandlers import CoreHandler, CoreUdpHandler from core.api.tlv.coreserver import CoreServer, CoreUdpServer from core.api.tlv.enumerations import CORE_API_PORT from core.constants import CORE_CONF_DIR, COREDPY_VERSION from core.utils import close_onexec, load_logging_config def banner(): """ Output the program banner printed to the terminal or log file. :return: nothing """ logging.info("CORE daemon v.%s started %s", constants.COREDPY_VERSION, time.ctime()) def start_udp(mainserver, server_address): """ Start a thread running a UDP server on the same host,port for connectionless requests. :param CoreServer mainserver: main core tcp server to piggy back off of :param server_address: :return: CoreUdpServer """ mainserver.udpserver = CoreUdpServer(server_address, CoreUdpHandler, mainserver) mainserver.udpthread = threading.Thread(target=mainserver.udpserver.start, daemon=True) mainserver.udpthread.start() def cored(cfg): """ Start the CoreServer object and enter the server loop. :param dict cfg: core configuration :return: nothing """ host = cfg["listenaddr"] port = int(cfg["port"]) if host == "" or host is None: host = "localhost" try: address = (host, port) server = CoreServer(address, CoreHandler, cfg) except: logging.exception("error starting main server on: %s:%s", host, port) sys.exit(1) # initialize grpc api grpc_server = CoreGrpcServer(server.coreemu) address_config = cfg["grpcaddress"] port_config = cfg["grpcport"] grpc_address = f"{address_config}:{port_config}" grpc_thread = threading.Thread(target=grpc_server.listen, args=(grpc_address,), daemon=True) grpc_thread.start() # start udp server start_udp(server, address) # close handlers close_onexec(server.fileno()) logging.info("CORE TLV API TCP/UDP listening on: %s:%s", host, port) server.serve_forever() def get_merged_config(filename): """ Return a configuration after merging config file and command-line arguments. :param str filename: file name to merge configuration settings with :return: merged configuration :rtype: dict """ # these are the defaults used in the config file default_log = os.path.join(constants.CORE_CONF_DIR, "logging.conf") default_grpc_port = "50051" default_address = "localhost" defaults = { "port": str(CORE_API_PORT), "listenaddr": default_address, "grpcport": default_grpc_port, "grpcaddress": default_address, "logfile": default_log } parser = argparse.ArgumentParser( description=f"CORE daemon v.{COREDPY_VERSION} instantiates Linux network namespace nodes.") parser.add_argument("-f", "--configfile", dest="configfile", help=f"read config from specified file; default = {filename}") parser.add_argument("-p", "--port", dest="port", type=int, help=f"port number to listen on; default = {CORE_API_PORT}") parser.add_argument("--ovs", action="store_true", help="enable experimental ovs mode, default is false") parser.add_argument("--grpc-port", dest="grpcport", help=f"grpc port to listen on; default {default_grpc_port}") parser.add_argument("--grpc-address", dest="grpcaddress", help=f"grpc address to listen on; default {default_address}") parser.add_argument("-l", "--logfile", help=f"core logging configuration; default {default_log}") # parse command line options args = parser.parse_args() # convert ovs to internal format args.ovs = "1" if args.ovs else "0" # read the config file if args.configfile is not None: filename = args.configfile del args.configfile cfg = ConfigParser(defaults) cfg.read(filename) section = "core-daemon" if not cfg.has_section(section): cfg.add_section(section) # merge argparse with configparser for opt in vars(args): val = getattr(args, opt) if val is not None: cfg.set(section, opt, str(val)) return dict(cfg.items(section)) def main(): """ Main program startup. :return: nothing """ cfg = get_merged_config(f"{CORE_CONF_DIR}/core.conf") load_logging_config(cfg["logfile"]) banner() try: cored(cfg) except KeyboardInterrupt: logging.info("keyboard interrupt, stopping core daemon") if __name__ == "__main__": main()