Esempio n. 1
0
def test_args():
    arg_parser = ArgumentParser(description="resoto metrics exporter",
                                env_args_prefix="RESOTOMETRICS_")
    add_args(arg_parser)
    arg_parser.parse_args()
    assert resotocore.http_uri == "https://localhost:8900"
    assert resotocore.ws_uri == "wss://localhost:8900"
Esempio n. 2
0
def test_args():
    arg_parser = ArgumentParser(
        description="resoto worker",
        env_args_prefix="RESOTOWORKER_",
    )
    add_args(arg_parser)
    core_add_args(arg_parser)
    arg_parser.parse_args()
    assert resotocore.http_uri == "https://localhost:8900"
Esempio n. 3
0
def test_args():
    arg_parser = ArgumentParser(description="resoto metrics exporter",
                                env_args_prefix="RESOTOMETRICS_")
    add_args(arg_parser)
    arg_parser.parse_args()
    assert ArgumentParser.args.resotocore_uri == "http://localhost:8900"
    assert ArgumentParser.args.resotocore_ws_uri == "ws://localhost:8900"
    assert ArgumentParser.args.resotocore_graph == "resoto"
    assert ArgumentParser.args.timeout == 300
Esempio n. 4
0
def main() -> None:
    setup_logger("resotometrics")

    signal(SIGINT, handler)
    signal(SIGTERM, handler)

    arg_parser = ArgumentParser(description="resoto metrics exporter",
                                env_args_prefix="RESOTOMETRICS_")
    add_args(arg_parser)
    logging_add_args(arg_parser)
    jwt_add_args(arg_parser)
    WebServer.add_args(arg_parser)
    WebApp.add_args(arg_parser)
    arg_parser.parse_args()

    metrics = Metrics()
    graph_collector = GraphCollector(metrics)
    REGISTRY.register(graph_collector)

    base_uri = ArgumentParser.args.resotocore_uri.strip("/")
    resotocore_graph = ArgumentParser.args.resotocore_graph
    graph_uri = f"{base_uri}/graph/{resotocore_graph}"
    query_uri = f"{graph_uri}/query/aggregate?section=reported"

    message_processor = partial(core_actions_processor, metrics, query_uri)
    core_actions = CoreActions(
        identifier="resotometrics",
        resotocore_uri=ArgumentParser.args.resotocore_uri,
        resotocore_ws_uri=ArgumentParser.args.resotocore_ws_uri,
        actions={
            "generate_metrics": {
                "timeout": ArgumentParser.args.timeout,
                "wait_for_completion": True,
            },
        },
        message_processor=message_processor,
    )
    web_server = WebServer(WebApp())
    web_server.daemon = True
    web_server.start()
    core_actions.start()
    shutdown_event.wait()
    web_server.shutdown()
    core_actions.shutdown()
    sys.exit(0)
Esempio n. 5
0
def test_web():
    arg_parser = ArgumentParser(
        description="resoto metrics exporter", env_args_prefix="RESOTOMETRICS_"
    )
    WebServer.add_args(arg_parser)
    WebApp.add_args(arg_parser)
    arg_parser.parse_args()

    # Find a free local port to reuse when we bind the web server.
    # This is so that multiple builds/tests can run in parallel
    # on the same CI agent.
    tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    tcp.bind(("", 0))
    _, free_port = tcp.getsockname()
    ArgumentParser.args.web_port = free_port
    tcp.close()
    # todo: race between closing socket and reusing free port in WebServer

    web_server = WebServer(WebApp())
    web_server.daemon = True
    web_server.start()
    start_time = time.time()
    while not web_server.serving:
        if time.time() - start_time > 10:
            raise RuntimeError("timeout waiting for web server start")
        time.sleep(0.1)

    # We're statically using localhost in the endpoint url.
    # Other options would have been to set ArgumentParser.args.web_host
    # and then connect to that value. However we'd have to use an IP
    # address and then needed to decide if we use either
    # 127.0.0.1 or ::1. Which might fail on CI boxes without
    # IPv4 or IPv6 respectively. Instead we leave the default which
    # binds to all IPs and assume that localhost will resolve to
    # the appropriate v4 or v6 loopback address. A disadvantage
    # of this is that for a brief moment during the test we're
    # exposing the web server on all local IPs.
    endpoint = f"http://localhost:{ArgumentParser.args.web_port}"
    r = requests.get(f"{endpoint}/health")
    assert r.content == b"ok\r\n"
    web_server.shutdown()
Esempio n. 6
0
def parse_args(args: Optional[List[str]] = None) -> Namespace:
    parser = ArgumentParser(
        env_args_prefix="RESOTOEVENTLOG_",
        description="Resoto Log Aggregator.",
    )
    jwt.add_args(parser)
    parser.add_argument("--no-tls",
                        default=False,
                        action="store_true",
                        help="Disable TLS and use plain HTTP.")
    parser.add_argument("--cert", help="Path to custom certificate file")
    parser.add_argument("--cert-key",
                        help="Path to custom certificate key file")
    parser.add_argument("--cert-key-pass",
                        help="Passphrase for certificate key file")
    parser.add_argument("--verbose",
                        action="store_true",
                        help="Enable verbose logging.")
    parser.add_argument("--debug",
                        action="store_true",
                        help="Enable verbose logging.")
    parser.add_argument("--max-queued-entries", type=int, default=1000)
    parser.add_argument("--version", action="store_true", help="Show version.")
    parser.add_argument("--host", default="0.0.0.0", help="Host to listen on.")
    parser.add_argument("--port",
                        default=8901,
                        type=int,
                        help="Port to listen on.")
    parser.add_argument(
        "--resotocore-uri",
        dest="resotocore_uri",
        default="https://localhost:8900",
        help="Resoto Core URI.",
    )
    parsed: Namespace = parser.parse_args(args if args else [])
    if parsed.version:
        print(f"resotoeventlog {version()}")
        sys.exit(0)
    return parsed
Esempio n. 7
0
def parse_args(args: Optional[List[str]] = None, namespace: Optional[str] = None) -> Namespace:
    def is_file(message: str) -> Callable[[str], str]:
        def check_file(path: str) -> str:
            if os.path.isfile(path):
                return path
            else:
                raise AttributeError(f"{message}: path {path} is not a directory!")

        return check_file

    def is_dir(message: str) -> Callable[[str], str]:
        def check_dir(path: str) -> str:
            if os.path.isdir(path):
                return path
            else:
                raise AttributeError(f"{message}: path {path} is not a directory!")

        return check_dir

    def is_url(message: str) -> Callable[[str], str]:
        def check_url(url: str) -> str:
            try:
                urlparse(url)
                return url
            except ValueError as ex:
                raise AttributeError(f"{message}: url {url} can not be parsed!") from ex

        return check_url

    parser = ArgumentParser(
        env_args_prefix="RESOTOCORE_",
        description="Maintains graphs of resources of any shape.",
        epilog="Keeps all the things.",
    )
    jwt_add_args(parser)
    parser.add_argument(
        "--log-level",
        default="info",
        help="Log level (default: info)",
    )
    parser.add_argument(
        "--graphdb-server",
        default="http://localhost:8529",
        dest="graphdb_server",
        help="Graph database server (default: http://localhost:8529)",
    )
    parser.add_argument(
        "--graphdb-database",
        default="resoto",
        dest="graphdb_database",
        help="Graph database name (default: resoto)",
    )
    parser.add_argument(
        "--graphdb-username",
        default="resoto",
        dest="graphdb_username",
        help="Graph database login (default: resoto)",
    )
    parser.add_argument(
        "--graphdb-password",
        default="",
        dest="graphdb_password",
        help='Graph database password (default: "")',
    )
    parser.add_argument(
        "--graphdb-root-password",
        default="",
        dest="graphdb_root_password",
        help="Graph root database password used for creating user and database if not existent.",
    )
    parser.add_argument(
        "--graphdb-bootstrap-do-not-secure",
        default=False,
        action="store_true",
        dest="graphdb_bootstrap_do_not_secure",
        help="Leave an empty root password during system setup process.",
    )
    parser.add_argument(
        "--graphdb-type",
        default="arangodb",
        dest="graphdb_type",
        help="Graph database type (default: arangodb)",
    )
    parser.add_argument(
        "--graphdb-no-ssl-verify",
        action="store_true",
        dest="graphdb_no_ssl_verify",
        help="If the connection should not be verified (default: False)",
    )
    parser.add_argument(
        "--graphdb-request-timeout",
        type=int,
        default=900,
        dest="graphdb_request_timeout",
        help="Request timeout in seconds (default: 900)",
    )
    parser.add_argument(
        "--plantuml-server",
        default="http://plantuml.resoto.org:8080",
        help="PlantUML server URI for UML image rendering.",
    )
    parser.add_argument(
        "--host",
        type=str,
        default="localhost",
        nargs="+",
        help="TCP host(s) to bind on (default: localhost)",
    )
    parser.add_argument(
        "--port",
        type=int,
        default=8900,
        help="TCP port to bind on (default: 8900)",
    )
    parser.add_argument(
        "--merge_max_wait_time_seconds",
        type=int,
        default=3600,
        help="Max waiting time to complete a merge graph action.",
    )
    parser.add_argument("--debug", default=False, action="store_true", help=argparse.SUPPRESS)
    parser.add_argument(
        "--analytics-opt-out",
        default=False,
        action="store_true",
        help="Stop collecting analytics data.",
    )
    parser.add_argument(
        "--ui-path",
        type=is_dir("can not parse --ui-dir"),
        help="The directory where the UI is installed. This directory will be served under /ui/.",
    )
    parser.add_argument(
        "--tsdb-proxy-url",
        type=is_url("can not parse --tsdb-proxy-url"),
        help="The url to the time series database. This path will be served under /tsdb/.",
    )
    parser.add_argument(
        "--tls-cert",
        type=is_file("can not parse --tls-cert"),
        help="Path to a single file in PEM format containing the certificate as well as any number "
        "of CA certificates needed to establish the certificate’s authenticity.",
    )
    parser.add_argument(
        "--tls-key",
        type=is_file("can not parse --tls-key"),
        help="Path to a file containing the private key. "
        "If not defined the private key will be taken from certfile as well.",
    )
    parser.add_argument(
        "--tls-password",
        type=str,
        help="Optional password to decrypt the private key file.",
    )
    parser.add_argument(
        "--cli-default-graph",
        type=str,
        default="resoto",
        dest="cli_default_graph",
        help="Use this graph for CLI actions, if no graph is specified explicitly.",
    )
    parser.add_argument(
        "--cli-default-section",
        type=str,
        default="reported",
        dest="cli_default_section",
        help="Use this graph section by default, if no section is specified."
        "Relative paths will be interpreted with respect to this section.",
    )
    parser.add_argument("--version", action="store_true", help="Print the version of resotocore and exit.")
    parser.add_argument(
        "--jobs",
        nargs="*",
        type=argparse.FileType("r"),
        help="Read job definitions from given file.",
    )
    parser.add_argument(
        "--start-collect-on-subscriber-connect",
        default=False,
        action="store_true",
        help="Start the collect workflow, when the first handling actor connects to the system.",
    )
    parser.add_argument(
        "---graph-update-abort-after",
        dest="graph_updates_abort_after",
        default="4h",
        type=parse_duration,
        help="If a graph update takes longer than this duration, the update is aborted.",
    )
    parsed: Namespace = parser.parse_args(args if args else [], namespace)

    if parsed.version:
        # print here on purpose, since logging is not set up yet.
        print(f"resotocore {version()}")
        sys.exit(0)

    return parsed
Esempio n. 8
0
def main() -> None:
    setup_logger("resotoworker")
    # Try to run in a new process group and
    # ignore if not possible for whatever reason
    try:
        os.setpgid(0, 0)
    except Exception:
        pass

    resotolib.signal.parent_pid = os.getpid()

    # Add cli args
    # The following double parsing of cli args is done so that when
    # a user specifies e.g. `--collector aws --help`  they would
    # no longer be shown cli args for other collectors like gcp.
    collector_arg_parser = ArgumentParser(
        description="resoto worker",
        env_args_prefix="RESOTOWORKER_",
        add_help=False,
        add_machine_help=False,
    )
    PluginLoader.add_args(collector_arg_parser)
    (args, _) = collector_arg_parser.parse_known_args()
    ArgumentParser.args = args

    arg_parser = ArgumentParser(
        description="resoto worker",
        env_args_prefix="RESOTOWORKER_",
    )
    jwt_add_args(arg_parser)
    logging_add_args(arg_parser)
    graph_add_args(arg_parser)
    collect_add_args(arg_parser)
    cleanup_add_args(arg_parser)
    core_add_args(arg_parser)
    resotocore_add_args(arg_parser)
    CoreActions.add_args(arg_parser)
    WebApp.add_args(arg_parser)
    PluginLoader.add_args(arg_parser)
    event_add_args(arg_parser)
    add_args(arg_parser)

    # Find resoto Plugins in the resoto.plugins module
    plugin_loader = PluginLoader()
    plugin_loader.add_plugin_args(arg_parser)

    # At this point the CLI, all Plugins as well as the WebServer have
    # added their args to the arg parser
    arg_parser.parse_args()

    # Handle Ctrl+c and other means of termination/shutdown
    resotolib.signal.initializer()
    add_event_listener(EventType.SHUTDOWN, shutdown, blocking=False)

    # Try to increase nofile and nproc limits
    increase_limits()

    web_server = WebServer(WebApp())
    web_server.daemon = True
    web_server.start()

    core_actions = CoreActions(
        identifier=f"{ArgumentParser.args.resotocore_subscriber_id}-collect_cleanup",
        resotocore_uri=ArgumentParser.args.resotocore_uri,
        resotocore_ws_uri=ArgumentParser.args.resotocore_ws_uri,
        actions={
            "collect": {
                "timeout": ArgumentParser.args.timeout,
                "wait_for_completion": True,
            },
            "cleanup": {
                "timeout": ArgumentParser.args.timeout,
                "wait_for_completion": True,
            },
        },
        message_processor=partial(
            core_actions_processor, plugin_loader.plugins(PluginType.COLLECTOR)
        ),
    )

    task_queue_filter = {}
    if ArgumentParser.args.collector and len(ArgumentParser.args.collector) > 0:
        task_queue_filter = {"cloud": list(ArgumentParser.args.collector)}
    core_tasks = CoreTasks(
        identifier="workerd-tasks",
        resotocore_ws_uri=ArgumentParser.args.resotocore_ws_uri,
        tasks=["tag"],
        task_queue_filter=task_queue_filter,
        message_processor=core_tag_tasks_processor,
    )
    core_actions.start()
    core_tasks.start()

    for Plugin in plugin_loader.plugins(PluginType.ACTION):
        try:
            log.debug(f"Starting action plugin {Plugin}")
            plugin = Plugin()
            plugin.start()
        except Exception as e:
            log.exception(f"Caught unhandled persistent Plugin exception {e}")

    # We wait for the shutdown Event to be set() and then end the program
    # While doing so we print the list of active threads once per 15 minutes
    shutdown_event.wait()
    web_server.shutdown()
    time.sleep(1)  # everything gets 1000ms to shutdown gracefully before we force it
    resotolib.signal.kill_children(resotolib.signal.SIGTERM, ensure_death=True)
    log.info("Shutdown complete")
    os._exit(0)
Esempio n. 9
0
def main() -> None:
    setup_logger("resotoshell")
    shutdown_event = Event()
    arg_parser = ArgumentParser(description="resoto shell",
                                env_args_prefix="RESOTOSHELL_")
    add_args(arg_parser)
    logging_add_args(arg_parser)
    jwt_add_args(arg_parser)
    arg_parser.parse_args()

    headers = {"Accept": "text/plain"}
    execute_endpoint = f"{ArgumentParser.args.resotocore_uri}/cli/execute"
    execute_endpoint += f"?resoto_session_id={rnd_str()}"
    if ArgumentParser.args.resotocore_graph:
        query_string = urlencode(
            {"graph": ArgumentParser.args.resotocore_graph})
        execute_endpoint += f"&{query_string}"
    if ArgumentParser.args.resotocore_section:
        query_string = urlencode(
            {"section": ArgumentParser.args.resotocore_section})
        execute_endpoint += f"&{query_string}"

    if ArgumentParser.args.stdin:
        shell = Shell(execute_endpoint, False, "monochrome")
        log.debug("Reading commands from STDIN")
        try:
            for command in sys.stdin.readlines():
                command = command.rstrip()
                shell.handle_command(command, headers)
        except KeyboardInterrupt:
            pass
        except (RuntimeError, ValueError) as e:
            log.error(e)
        except Exception:
            log.exception(
                "Caught unhandled exception while processing CLI command")
        finally:
            shutdown_event.set()
    else:
        shell = Shell(execute_endpoint, True, detect_color_system())
        completer = None
        history_file = str(pathlib.Path.home() / ".resotoshell_history")
        history = FileHistory(history_file)
        session = PromptSession(history=history)
        log.debug("Starting interactive session")

        while not shutdown_event.is_set():
            try:
                command = session.prompt("> ", completer=completer)
                if command == "":
                    continue
                if command == "quit":
                    shutdown_event.set()
                    continue

                shell.handle_command(command, headers)

            except KeyboardInterrupt:
                pass
            except EOFError:
                shutdown_event.set()
            except (RuntimeError, ValueError) as e:
                log.error(e)
            except Exception:
                log.exception(
                    "Caught unhandled exception while processing CLI command")

    sys.exit(0)
Esempio n. 10
0
def main() -> None:
    setup_logger("resotoworker")
    # Try to run in a new process group and
    # ignore if not possible for whatever reason
    try:
        os.setpgid(0, 0)
    except Exception:
        pass

    resotolib.proc.parent_pid = os.getpid()

    arg_parser = ArgumentParser(
        description="resoto worker",
        env_args_prefix="RESOTOWORKER_",
    )
    add_args(arg_parser)
    jwt_add_args(arg_parser)
    logging_add_args(arg_parser)
    core_add_args(arg_parser)
    Config.add_args(arg_parser)
    TLSData.add_args(arg_parser)

    # Find resoto Plugins in the resoto.plugins module
    plugin_loader = PluginLoader()
    plugin_loader.add_plugin_args(arg_parser)

    # At this point the CLI, all Plugins as well as the WebServer have
    # added their args to the arg parser
    arg_parser.parse_args()

    try:
        wait_for_resotocore(resotocore.http_uri)
    except TimeoutError as e:
        log.fatal(f"Failed to connect to resotocore: {e}")
        sys.exit(1)

    tls_data = None
    if resotocore.is_secure:
        tls_data = TLSData(
            common_name=ArgumentParser.args.subscriber_id,
            resotocore_uri=resotocore.http_uri,
        )
        tls_data.start()
    config = Config(
        ArgumentParser.args.subscriber_id,
        resotocore_uri=resotocore.http_uri,
        tls_data=tls_data,
    )
    add_config(config)
    plugin_loader.add_plugin_config(config)
    config.load_config()

    def send_request(request: requests.Request) -> requests.Response:
        prepared = request.prepare()
        s = requests.Session()
        verify = None
        if tls_data:
            verify = tls_data.verify
        return s.send(request=prepared, verify=verify)

    core = Resotocore(send_request, config)

    collector = Collector(core.send_to_resotocore, config)

    # Handle Ctrl+c and other means of termination/shutdown
    resotolib.proc.initializer()
    add_event_listener(EventType.SHUTDOWN, shutdown, blocking=False)

    # Try to increase nofile and nproc limits
    increase_limits()

    web_server_args = {}
    if tls_data:
        web_server_args = {
            "ssl_cert": tls_data.cert_path,
            "ssl_key": tls_data.key_path,
        }
    web_server = WebServer(
        WebApp(mountpoint=Config.resotoworker.web_path),
        web_host=Config.resotoworker.web_host,
        web_port=Config.resotoworker.web_port,
        **web_server_args,
    )
    web_server.daemon = True
    web_server.start()

    core_actions = CoreActions(
        identifier=f"{ArgumentParser.args.subscriber_id}-collector",
        resotocore_uri=resotocore.http_uri,
        resotocore_ws_uri=resotocore.ws_uri,
        actions={
            "collect": {
                "timeout": Config.resotoworker.timeout,
                "wait_for_completion": True,
            },
            "cleanup": {
                "timeout": Config.resotoworker.timeout,
                "wait_for_completion": True,
            },
        },
        message_processor=partial(core_actions_processor, plugin_loader, tls_data, collector),
        tls_data=tls_data,
    )

    task_queue_filter = {}
    if len(Config.resotoworker.collector) > 0:
        task_queue_filter = {"cloud": list(Config.resotoworker.collector)}
    core_tasks = CoreTasks(
        identifier=f"{ArgumentParser.args.subscriber_id}-tagger",
        resotocore_ws_uri=resotocore.ws_uri,
        tasks=["tag"],
        task_queue_filter=task_queue_filter,
        message_processor=core_tag_tasks_processor,
        tls_data=tls_data,
    )
    core_actions.start()
    core_tasks.start()

    for Plugin in plugin_loader.plugins(PluginType.ACTION):
        try:
            log.debug(f"Starting action plugin {Plugin}")
            plugin = Plugin(tls_data=tls_data)
            plugin.start()
        except Exception as e:
            log.exception(f"Caught unhandled persistent Plugin exception {e}")

    # We wait for the shutdown Event to be set() and then end the program
    # While doing so we print the list of active threads once per 15 minutes
    shutdown_event.wait()
    web_server.shutdown()
    time.sleep(1)  # everything gets 1000ms to shutdown gracefully before we force it
    resotolib.proc.kill_children(resotolib.proc.SIGTERM, ensure_death=True)
    log.info("Shutdown complete")
    os._exit(0)
Esempio n. 11
0
def test_args():
    arg_parser = ArgumentParser(description="resoto Shell",
                                env_args_prefix="RESOTOSHELL_")
    add_args(arg_parser)
    arg_parser.parse_args()
    assert ArgumentParser.args.resotocore_uri == "http://localhost:8900"
Esempio n. 12
0
def parse_args(args: Optional[List[str]] = None) -> Namespace:
    def is_file(message: str) -> Callable[[str], str]:
        def check_file(path: str) -> str:
            if os.path.isfile(path):
                return path
            else:
                raise AttributeError(
                    f"{message}: path {path} is not a directory!")

        return check_file

    def is_dir(message: str) -> Callable[[str], str]:
        def check_dir(path: str) -> str:
            if os.path.isdir(path):
                return path
            else:
                raise AttributeError(
                    f"{message}: path {path} is not a directory!")

        return check_dir

    def key_value(kv: str) -> Tuple[str, JsonElement]:
        try:
            key, value = path_json_value_parser.parse(kv)
            return (key, value[0]) if len(value) == 1 else (key, value)
        except Exception as ex:
            raise AttributeError(
                f"Can not parse config option: {kv}. Reason: {ex}") from ex

    parser = ArgumentParser(
        env_args_prefix="RESOTOCORE_",
        description="Maintains graphs of resources of any shape.",
        epilog="Keeps all the things.",
    )
    jwt_add_args(parser)
    parser.add_argument(
        "--graphdb-server",
        default="http://localhost:8529",
        dest="graphdb_server",
        help="Graph database server (default: http://localhost:8529)",
    )
    parser.add_argument("--graphdb-database",
                        default="resoto",
                        dest="graphdb_database",
                        help="Graph database name (default: resoto)")
    parser.add_argument("--graphdb-username",
                        default="resoto",
                        dest="graphdb_username",
                        help="Graph database login (default: resoto)")
    parser.add_argument("--graphdb-password",
                        default="",
                        dest="graphdb_password",
                        help='Graph database password (default: "")')
    parser.add_argument(
        "--graphdb-root-password",
        default="",
        dest="graphdb_root_password",
        help=
        "Graph root database password used for creating user and database if not existent.",
    )
    parser.add_argument(
        "--graphdb-bootstrap-do-not-secure",
        default=False,
        action="store_true",
        dest="graphdb_bootstrap_do_not_secure",
        help="Leave an empty root password during system setup process.",
    )
    parser.add_argument("--graphdb-type",
                        default="arangodb",
                        dest="graphdb_type",
                        help="Graph database type (default: arangodb)")
    parser.add_argument(
        "--graphdb-no-ssl-verify",
        action="store_true",
        dest="graphdb_no_ssl_verify",
        help="If the connection should not be verified (default: False)",
    )
    parser.add_argument(
        "--graphdb-request-timeout",
        type=int,
        default=900,
        dest="graphdb_request_timeout",
        help="Request timeout in seconds (default: 900)",
    )
    parser.add_argument("--no-tls",
                        default=False,
                        action="store_true",
                        help="Disable TLS and use plain HTTP.")
    parser.add_argument(
        "--cert",
        type=is_file("can not parse --cert"),
        dest="cert",
        help=
        "Path to a single file in PEM format containing the host certificate. "
        "If no certificate is provided, it is created using the CA.",
    )
    parser.add_argument(
        "--cert-key",
        type=is_file("can not parse --cert-key"),
        dest="cert_key",
        help=
        "In case a --cert is provided. Path to a file containing the private key.",
    )
    parser.add_argument(
        "--cert-key-pass",
        dest="cert_key_pass",
        type=str,
        help=
        "In case a --cert is provided. Optional password to decrypt the private key file.",
    )
    parser.add_argument(
        "--ca-cert",
        type=is_file("can not parse --ca-cert"),
        dest="ca_cert",
        help=
        "Path to a single file in PEM format containing the CA certificate.",
    )
    parser.add_argument(
        "--ca-cert-key",
        type=is_file("can not parse --ca-cert-key"),
        dest="ca_cert_key",
        help="Path to a file containing the private key for the CA certificate. "
        "New certificates can be created when a CA certificate and private key is provided. "
        "Without the private key, the CA certificate is only used for outgoing http requests.",
    )
    parser.add_argument(
        "--ca-cert-key-pass",
        dest="ca_cert_key_pass",
        type=str,
        help="Optional password to decrypt the private ca-cert-key file.",
    )
    parser.add_argument("--version",
                        action="store_true",
                        help="Print the version of resotocore and exit.")
    parser.add_argument(
        "--override",
        "-o",
        nargs="+",
        type=key_value,
        dest="config_override",
        default=[],
        help=
        "Override configuration parameters. Format: path.to.property=value. "
        "The existing configuration will be patched with the provided values. "
        "A value can be a simple value or a comma separated list of values if a list is required. "
        "Note: this argument allows multiple overrides separated by space. "
        "Example: --override resotocore.api.web_hosts=localhost,some.domain resotocore.api.web_port=12345",
    )
    parser.add_argument("--verbose",
                        "-v",
                        dest="verbose",
                        default=False,
                        action="store_true",
                        help="Enable verbose logging.")
    parser.add_argument(  # No default here on purpose: it can be reconfigured!
        "--debug",
        default=None,
        action="store_true",
        help="Enable debug mode. If not defined use configuration.")
    parser.add_argument(  # No default here on purpose: it can be reconfigured!
        "--ui-path",
        type=is_dir("can not parse --ui-dir"),
        help="Path to the UI files. If not defined use configuration..",
    )
    parser.add_argument("--analytics-opt-out",
                        default=None,
                        action="store_true",
                        help=argparse.SUPPRESS)
    parser.add_argument("--resotoeventlog-uri",
                        dest="resotoeventlog_uri",
                        default=None,
                        help="URI to the resotoeventlog server.")

    parsed: Namespace = parser.parse_args(args if args else [])

    if parsed.version:
        # print here on purpose, since logging is not set up yet.
        print(f"resotocore {version()}")
        sys.exit(0)

    return parsed
Esempio n. 13
0
def main() -> None:
    setup_logger("resotometrics")
    resotolib.proc.parent_pid = os.getpid()

    add_event_listener(EventType.SHUTDOWN, shutdown)
    arg_parser = ArgumentParser(description="resoto metrics exporter",
                                env_args_prefix="RESOTOMETRICS_")
    add_args(arg_parser)
    Config.add_args(arg_parser)
    resotocore_add_args(arg_parser)
    logging_add_args(arg_parser)
    jwt_add_args(arg_parser)
    TLSData.add_args(arg_parser)
    arg_parser.parse_args()

    try:
        wait_for_resotocore(resotocore.http_uri)
    except TimeoutError as e:
        log.fatal(f"Failed to connect to resotocore: {e}")
        sys.exit(1)

    tls_data = None
    if resotocore.is_secure:
        tls_data = TLSData(
            common_name=ArgumentParser.args.subscriber_id,
            resotocore_uri=resotocore.http_uri,
        )
        tls_data.start()
    config = Config(
        ArgumentParser.args.subscriber_id,
        resotocore_uri=resotocore.http_uri,
        tls_data=tls_data,
    )
    config.add_config(ResotoMetricsConfig)
    config.load_config()

    resotolib.proc.initializer()

    metrics = Metrics()
    graph_collector = GraphCollector(metrics)
    REGISTRY.register(graph_collector)

    resotocore_graph = Config.resotometrics.graph
    graph_uri = f"{resotocore.http_uri}/graph/{resotocore_graph}"
    search_uri = f"{graph_uri}/search/aggregate?section=reported"

    message_processor = partial(core_actions_processor, metrics, search_uri,
                                tls_data)
    core_actions = CoreActions(
        identifier=ArgumentParser.args.subscriber_id,
        resotocore_uri=resotocore.http_uri,
        resotocore_ws_uri=resotocore.ws_uri,
        actions={
            "generate_metrics": {
                "timeout": Config.resotometrics.timeout,
                "wait_for_completion": True,
            },
        },
        message_processor=message_processor,
        tls_data=tls_data,
    )
    web_server_args = {}
    if tls_data:
        web_server_args = {
            "ssl_cert": tls_data.cert_path,
            "ssl_key": tls_data.key_path,
        }
    web_server = WebServer(
        WebApp(mountpoint=Config.resotometrics.web_path),
        web_host=Config.resotometrics.web_host,
        web_port=Config.resotometrics.web_port,
        **web_server_args,
    )
    web_server.daemon = True
    web_server.start()
    core_actions.start()
    shutdown_event.wait()
    web_server.shutdown()
    core_actions.shutdown()
    resotolib.proc.kill_children(resotolib.proc.SIGTERM, ensure_death=True)
    log.info("Shutdown complete")
    sys.exit(0)