Beispiel #1
0
 def test_errors_on_invalid_elements(self, from_config: mock.Mock):
     from_config.side_effect = ConfigurationError()
     with self.assertRaisesRegex(ConfigurationError, 'element'):
         data_stream.from_config(self.base_config)
Beispiel #2
0
def validate_name(name: str) -> str:
    if name is None or len(name) == 0:
        raise ConfigurationError("missing name")
    if '/' in name:
        raise ConfigurationError("invalid name, '\\' not allowed")
    return name
Beispiel #3
0
def validate_name(name: str) -> str:
    if len(name) == 0:
        raise ConfigurationError("missing name")
    return name
Beispiel #4
0
def run(custom_values=None, verify=True) -> config.JouleConfig:
    """provide a dict INI configuration to override defaults
       if verify is True, perform checks on settings to make sure they are appropriate"""
    my_configs = configparser.ConfigParser()
    my_configs.read_dict(config.DEFAULT_CONFIG)
    if custom_values is not None:
        my_configs.read_dict(custom_values)

    main_config = my_configs['Main']
    # Node name
    node_name = main_config['Name']

    # ModuleDirectory
    module_directory = main_config['ModuleDirectory']
    if not os.path.isdir(module_directory) and verify:
        raise ConfigurationError(
            "ModuleDirectory [%s] does not exist" % module_directory)
    # StreamDirectory
    stream_directory = main_config['StreamDirectory']
    if not os.path.isdir(stream_directory) and verify:
        raise ConfigurationError(
            "StreamDirectory [%s] does not exist" % stream_directory)

    # Specify IPAddress and Port to listen on network interface
    # IPAddress
    if 'IPAddress' in main_config:
        ip_address = main_config['IPAddress']
        try:
            ipaddress.ip_address(ip_address)
        except ValueError as e:
            raise ConfigurationError("IPAddress is invalid") from e
        # Port
        try:
            port = int(main_config['Port'])
            if port < 0 or port > 65535:
                raise ValueError()
        except ValueError as e:
            raise ConfigurationError("Port must be between 0 - 65535") from e
    else:
        port = None
        ip_address = None

    # SocketDirectory
    socket_directory = main_config['SocketDirectory']
    if not os.path.isdir(socket_directory) and verify:
        try:
            os.mkdir(socket_directory)
            # make sure the ownership is correct
            os.chmod(socket_directory, 0o700)
        except FileExistsError:
            raise ConfigurationError("SocketDirectory [%s] is a file" % socket_directory)
        except PermissionError:
            raise ConfigurationError("Cannot create SocketDirectory at [%s]" % socket_directory)

    if not os.access(socket_directory, os.W_OK) and verify:
        raise ConfigurationError(
            "SocketDirectory [%s] is not writable" % socket_directory)

    # Nilmdb URL
    if 'NilmdbUrl' in main_config and main_config['NilmdbUrl'] != '':
        nilmdb_url = main_config['NilmdbUrl']
        if verify:
            loop = asyncio.get_event_loop()
            loop.run_until_complete(verify_nilmdb_url(nilmdb_url))

    else:
        nilmdb_url = None

    # Database
    if 'Database' in main_config:
        database = "postgresql://" + main_config['Database']
    elif verify:
        raise ConfigurationError("Missing [Database] configuration")
    else:  # pragma: no cover
        database = ''  # this is invalid of course, just used in unit testing
    if verify:
        # check to see if this is a valid database DSN
        try:
            conn = psycopg2.connect(database)
            conn.close()
        except psycopg2.Error:
            raise ConfigurationError("Cannot connect to database [%s]" % database)

    # InsertPeriod
    try:
        insert_period = int(main_config['InsertPeriod'])
        if insert_period <= 0:
            raise ValueError()
    except ValueError:
        raise ConfigurationError("InsertPeriod must be a postive number")

    # CleanupPeriod
    try:
        cleanup_period = int(main_config['CleanupPeriod'])
        if cleanup_period <= 0 or cleanup_period < insert_period:
            raise ValueError()
    except ValueError:
        raise ConfigurationError("CleanupPeriod must be a postive number > InsertPeriod")

    # Max Log Lines
    try:
        max_log_lines = int(main_config['MaxLogLines'])
        if max_log_lines <= 0:
            raise ValueError()
    except ValueError:
        raise ConfigurationError("MaxLogLines must be a postive number")

    # Security configuration
    if 'Security' in my_configs:
        security = config.SecurityConfig(my_configs['Security']['Certificate'],
                                         my_configs['Security']['Key'],
                                         my_configs.get('Security',
                                                        'CertificateAuthority', fallback=""))
    else:
        security = None

    # Proxies
    uuid = 0
    proxies = []
    if 'Proxies' in my_configs:
        for name in my_configs['Proxies']:
            url_str = my_configs['Proxies'][name]
            # make sure proxy url ends with /
            if url_str[-1] != '/':
                url_str += '/'
            url = yarl.URL(url_str)
            proxies.append(Proxy(name, uuid, url))
            uuid += 1

    return config.JouleConfig(
        name=node_name,
        module_directory=module_directory,
        stream_directory=stream_directory,
        ip_address=ip_address,
        port=port,
        socket_directory=socket_directory,
        database=database,
        insert_period=insert_period,
        cleanup_period=cleanup_period,
        max_log_lines=max_log_lines,
        nilmdb_url=nilmdb_url,
        proxies=proxies,
        security=security
    )
Beispiel #5
0
def main(argv=None):
    parser = argparse.ArgumentParser("Joule Daemon")
    parser.add_argument("--config", default="/etc/joule/main.conf")

    xargs = parser.parse_args(argv)

    log.addFilter(LogDedupFilter())
    logging.basicConfig(format='%(asctime)s %(levelname)s:%(message)s',
                        level=logging.WARNING)
    if xargs.config is not None:
        if os.path.isfile(xargs.config) is False:
            log.error("Invalid configuration: cannot load file [%s]" %
                      xargs.config)
            exit(1)

    my_config = None
    try:
        cparser = configparser.ConfigParser()
        r = cparser.read(xargs.config)
        if len(r) != 1:
            raise ConfigurationError(f"cannot read {xargs.config}")
        my_config = load_config.run(custom_values=cparser)
    except ConfigurationError as e:
        log.error("Invalid configuration: %s" % e)
        exit(1)

    # uvloop uses libuv which does not support
    # connections to abstract namespace sockets
    # https://github.com/joyent/libuv/issues/1486

    asyncio.set_event_loop_policy(uvloop.EventLoopPolicy())

    loop = asyncio.get_event_loop()
    loop.set_debug(True)
    daemon = Daemon(my_config)
    try:
        daemon.initialize()
    except SQLAlchemyError as e:
        print("""
        Error initializing database, ensure user 'joule' has sufficient permissions:
        From a shell run
        $> sudo -u postgres psql
        postgres=# ALTER ROLE joule WITH CREATEROLE REPLICATION;
        postgres=# GRANT ALL PRIVILEGES ON DATABASE joule TO joule WITH GRANT OPTION;
        postgres=# GRANT pg_read_all_settings TO joule;
        """)
        loop.close()
        exit(1)

    loop.add_signal_handler(signal.SIGINT, daemon.stop)
    loop.add_signal_handler(signal.SIGTERM, daemon.stop)

    async def debugger():
        task_list = []
        while True:
            for t in asyncio.all_tasks():
                name = t.get_name()

                if "Task" in name:
                    if "aiohttp" in str(t.get_stack()):
                        t.set_name("aiohttp")
                    elif "StreamReader.read" in str(t.get_coro()):
                        t.set_name("StreamReader.read")
                    else:
                        t.set_name(f"UNK {t.get_coro()}")

                if t not in task_list:
                    task_list.append(t)
                    # print(f"New Task[{id(t)}]: {t.get_name()}")

            for t in task_list:
                if t.done():
                    # print(f"DONE: {t.get_name()}: ", end="")
                    # if t.cancelled():
                    #    print("cancelled")
                    if not t.cancelled():
                        exception = t.exception()
                        if exception is not None:
                            print("Got exception", t.exception())
                            # print("----Cancelling Daemon----")
                            # daemon_task.cancel()
                        # else:
                        #    print("completed")
                    task_list.remove(t)
                # else:
                #    print(f"RUNNING: {t.get_name()}")

            await asyncio.sleep(2)

    debug = loop.create_task(debugger())
    daemon_task = loop.create_task(daemon.run())
    daemon_task.set_name("daemon")
    debug.set_name("debugger")
    loop.run_until_complete(daemon_task)
    debug.cancel()
    try:
        loop.run_until_complete(debug)
    except asyncio.CancelledError:
        pass
    loop.close()

    # clear out the socket directory
    for file_name in os.listdir(my_config.socket_directory):
        path = os.path.join(my_config.socket_directory, file_name)
        os.unlink(path)
    exit(0)
Beispiel #6
0
def _get_bool(setting: str, config: configparser.ConfigParser, default: bool):
    try:
        return config.getboolean(setting, default)
    except ValueError as e:
        raise ConfigurationError("[%s] invalid value, use True/False" %
                                 setting) from e
Beispiel #7
0
def validate_name(name: str) -> str:
    if name == "":
        raise ConfigurationError("missing name")
    return name