Ejemplo n.º 1
0
async def async_check_config(config_dir):
    """Check the OPP config."""
    opp = core.OpenPeerPower()
    opp.config.config_dir = config_dir
    components = await async_check_op_config_file(opp)
    await opp.async_stop(force=True)
    return components
Ejemplo n.º 2
0
async def test_track_task_functions(loop):
    """Test function to start/stop track task and initial state."""
    opp = ha.OpenPeerPower()
    try:
        assert opp._track_task

        opp.async_stop_track_tasks()
        assert not opp._track_task

        opp.async_track_tasks()
        assert opp._track_task
    finally:
        await opp.async_stop()
Ejemplo n.º 3
0
async def test_opp_start_starts_the_timer(loop):
    """Test when opp.starts, it starts the timer."""
    opp = ha.OpenPeerPower()

    try:
        with patch("openpeerpower.core._async_create_timer") as mock_timer:
            await opp.async_start()

        assert opp.state == ha.CoreState.running
        assert not opp._track_task
        assert len(mock_timer.mock_calls) == 1
        assert mock_timer.mock_calls[0][1][0] is opp

    finally:
        await opp.async_stop()
        assert opp.state == ha.CoreState.stopped
Ejemplo n.º 4
0
async def test_start_taking_too_long(loop, caplog):
    """Test when async_start takes too long."""
    opp = ha.OpenPeerPower()
    caplog.set_level(logging.WARNING)

    try:
        with patch.object(
                opp, "async_block_till_done",
                side_effect=asyncio.TimeoutError), patch(
                    "openpeerpower.core._async_create_timer") as mock_timer:
            await opp.async_start()

        assert opp.state == ha.CoreState.running
        assert len(mock_timer.mock_calls) == 1
        assert mock_timer.mock_calls[0][1][0] is opp
        assert "Something is blocking Open Peer Power" in caplog.text

    finally:
        await opp.async_stop()
        assert opp.state == ha.CoreState.stopped
Ejemplo n.º 5
0
def run(args):
    """Handle benchmark commandline script."""
    # Disable logging
    logging.getLogger("openpeerpower.core").setLevel(logging.CRITICAL)

    parser = argparse.ArgumentParser(description=("Run a Open Peer Power benchmark."))
    parser.add_argument("name", choices=BENCHMARKS)
    parser.add_argument("--script", choices=["benchmark"])

    args = parser.parse_args()

    bench = BENCHMARKS[args.name]

    print("Using event loop:", asyncio.get_event_loop_policy().__module__)

    with suppress(KeyboardInterrupt):
        while True:
            loop = asyncio.new_event_loop()
            opp = core.OpenPeerPower(loop)
            opp.async_stop_track_tasks()
            runtime = loop.run_until_complete(bench(opp))
            print(f"Benchmark {bench.__name__} done in {runtime}s")
            loop.run_until_complete(opp.async_stop())
            loop.close()
Ejemplo n.º 6
0
async def run_benchmark(bench):
    """Run a benchmark."""
    opp = core.OpenPeerPower()
    runtime = await bench(opp)
    print(f"Benchmark {bench.__name__} done in {runtime}s")
    await opp.async_stop()
Ejemplo n.º 7
0
async def async_setup_opp(
    *,
    config_dir: str,
    verbose: bool,
    log_rotate_days: int,
    log_file: str,
    log_no_color: bool,
    skip_pip: bool,
    safe_mode: bool,
) -> Optional[core.OpenPeerPower]:
    """Set up Open Peer Power."""
    opp = core.OpenPeerPower()
    opp.config.config_dir = config_dir

    async_enable_logging(opp, verbose, log_rotate_days, log_file, log_no_color)

    opp.config.skip_pip = skip_pip
    if skip_pip:
        _LOGGER.warning(
            "Skipping pip installation of required modules. This may cause issues"
        )

    if not await conf_util.async_ensure_config_exists(opp):
        _LOGGER.error("Error getting configuration path")
        return None

    _LOGGER.info("Config directory: %s", config_dir)

    config_dict = None
    basic_setup_success = False

    if not safe_mode:
        await opp.async_add_executor_job(conf_util.process_op_config_upgrade, opp)

        try:
            config_dict = await conf_util.async_opp_config_yaml(opp)
        except OpenPeerPowerError as err:
            _LOGGER.error(
                "Failed to parse configuration.yaml: %s. Activating safe mode",
                err,
            )
        else:
            if not is_virtual_env():
                await async_mount_local_lib_path(config_dir)

            basic_setup_success = (
                await async_from_config_dict(config_dict, opp) is not None
            )
        finally:
            clear_secret_cache()

    if config_dict is None:
        safe_mode = True

    elif not basic_setup_success:
        _LOGGER.warning("Unable to set up core integrations. Activating safe mode")
        safe_mode = True

    elif (
        "frontend" in opp.data.get(DATA_SETUP, {})
        and "frontend" not in opp.config.components
    ):
        _LOGGER.warning("Detected that frontend did not load. Activating safe mode")
        # Ask integrations to shut down. It's messy but we can't
        # do a clean stop without knowing what is broken
        opp.async_track_tasks()
        opp.bus.async_fire(EVENT_OPENPEERPOWER_STOP, {})
        with contextlib.suppress(asyncio.TimeoutError):
            async with timeout(10):
                await opp.async_block_till_done()

        safe_mode = True
        opp = core.OpenPeerPower()
        opp.config.config_dir = config_dir

    if safe_mode:
        _LOGGER.info("Starting in safe mode")
        opp.config.safe_mode = True

        http_conf = (await http.async_get_last_config(opp)) or {}

        await async_from_config_dict(
            {"safe_mode": {}, "http": http_conf},
            opp,
        )

    return opp
Ejemplo n.º 8
0
async def async_test_open_peer_power(loop, load_registries=True):
    """Return a Open Peer Power object pointing at test config dir."""
    opp = ha.OpenPeerPower()
    store = auth_store.AuthStore(opp)
    opp.auth = auth.AuthManager(opp, store, {}, {})
    ensure_auth_manager_loaded(opp.auth)
    INSTANCES.append(opp)

    orig_async_add_job = opp.async_add_job
    orig_async_add_executor_job = opp.async_add_executor_job
    orig_async_create_task = opp.async_create_task

    def async_add_job(target, *args):
        """Add job."""
        check_target = target
        while isinstance(check_target, ft.partial):
            check_target = check_target.func

        if isinstance(check_target,
                      Mock) and not isinstance(target, AsyncMock):
            fut = asyncio.Future()
            fut.set_result(target(*args))
            return fut

        return orig_async_add_job(target, *args)

    def async_add_executor_job(target, *args):
        """Add executor job."""
        check_target = target
        while isinstance(check_target, ft.partial):
            check_target = check_target.func

        if isinstance(check_target, Mock):
            fut = asyncio.Future()
            fut.set_result(target(*args))
            return fut

        return orig_async_add_executor_job(target, *args)

    def async_create_task(coroutine):
        """Create task."""
        if isinstance(coroutine,
                      Mock) and not isinstance(coroutine, AsyncMock):
            fut = asyncio.Future()
            fut.set_result(None)
            return fut

        return orig_async_create_task(coroutine)

    async def async_wait_for_task_count(self,
                                        max_remaining_tasks: int = 0) -> None:
        """Block until at most max_remaining_tasks remain.

        Based on OpenPeerPower.async_block_till_done
        """
        # To flush out any call_soon_threadsafe
        await asyncio.sleep(0)
        start_time: float | None = None

        while len(self._pending_tasks) > max_remaining_tasks:
            pending: Collection[Awaitable[Any]] = [
                task for task in self._pending_tasks if not task.done()
            ]
            self._pending_tasks.clear()
            if len(pending) > max_remaining_tasks:
                remaining_pending = await self._await_count_and_log_pending(
                    pending, max_remaining_tasks=max_remaining_tasks)
                self._pending_tasks.extend(remaining_pending)

                if start_time is None:
                    # Avoid calling monotonic() until we know
                    # we may need to start logging blocked tasks.
                    start_time = 0
                elif start_time == 0:
                    # If we have waited twice then we set the start
                    # time
                    start_time = monotonic()
                elif monotonic() - start_time > BLOCK_LOG_TIMEOUT:
                    # We have waited at least three loops and new tasks
                    # continue to block. At this point we start
                    # logging all waiting tasks.
                    for task in pending:
                        _LOGGER.debug("Waiting for task: %s", task)
            else:
                self._pending_tasks.extend(pending)
                await asyncio.sleep(0)

    async def _await_count_and_log_pending(
            self,
            pending: Collection[Awaitable[Any]],
            max_remaining_tasks: int = 0) -> Collection[Awaitable[Any]]:
        """Block at most max_remaining_tasks remain and log tasks that take a long time.

        Based on OpenPeerPower._await_and_log_pending
        """
        wait_time = 0

        return_when = asyncio.ALL_COMPLETED
        if max_remaining_tasks:
            return_when = asyncio.FIRST_COMPLETED

        while len(pending) > max_remaining_tasks:
            _, pending = await asyncio.wait(pending,
                                            timeout=BLOCK_LOG_TIMEOUT,
                                            return_when=return_when)
            if not pending or max_remaining_tasks:
                return pending
            wait_time += BLOCK_LOG_TIMEOUT
            for task in pending:
                _LOGGER.debug("Waited %s seconds for task: %s", wait_time,
                              task)

        return []

    opp.async_add_job = async_add_job
    opp.async_add_executor_job = async_add_executor_job
    opp.async_create_task = async_create_task
    opp.async_wait_for_task_count = types.MethodType(async_wait_for_task_count,
                                                     opp)
    opp._await_count_and_log_pending = types.MethodType(
        _await_count_and_log_pending, opp)

    opp.data[loader.DATA_CUSTOM_COMPONENTS] = {}

    opp.config.location_name = "test home"
    opp.config.config_dir = get_test_config_dir()
    opp.config.latitude = 32.87336
    opp.config.longitude = -117.22743
    opp.config.elevation = 0
    opp.config.time_zone = "US/Pacific"
    opp.config.units = METRIC_SYSTEM
    opp.config.media_dirs = {"local": get_test_config_dir("media")}
    opp.config.skip_pip = True

    opp.config_entries = config_entries.ConfigEntries(opp, {})
    opp.config_entries._entries = {}
    opp.config_entries._store._async_ensure_stop_listener = lambda: None

    # Load the registries
    if load_registries:
        await asyncio.gather(
            device_registry.async_load(opp),
            entity_registry.async_load(opp),
            area_registry.async_load(opp),
        )
        await opp.async_block_till_done()

    opp.state = ha.CoreState.running

    # Mock async_start
    orig_start = opp.async_start

    async def mock_async_start():
        """Start the mocking."""
        # We only mock time during tests and we want to track tasks
        with patch("openpeerpower.core._async_create_timer"), patch.object(
                opp, "async_stop_track_tasks"):
            await orig_start()

    opp.async_start = mock_async_start

    @ha.callback
    def clear_instance(event):
        """Clear global instance."""
        INSTANCES.remove(opp)

    opp.bus.async_listen_once(EVENT_OPENPEERPOWER_CLOSE, clear_instance)

    return opp
Ejemplo n.º 9
0
def check(config_dir, secrets=False):
    """Perform a check by mocking opp load functions."""
    logging.getLogger("openpeerpower.loader").setLevel(logging.CRITICAL)
    res: Dict[str, Any] = {
        "yaml_files": OrderedDict(),  # yaml_files loaded
        "secrets": OrderedDict(),  # secret cache and secrets loaded
        "except": OrderedDict(),  # exceptions raised (with config)
        #'components' is a OpenPeerPowerConfig  # noqa: E265
        "secret_cache": None,
    }

    # pylint: disable=possibly-unused-variable
    def mock_load(filename):
        """Mock opp.util.load_yaml to save config file names."""
        res["yaml_files"][filename] = True
        return MOCKS["load"][1](filename)

    # pylint: disable=possibly-unused-variable
    def mock_secrets(ldr, node):
        """Mock _get_secrets."""
        try:
            val = MOCKS["secrets"][1](ldr, node)
        except OpenPeerPowerError:
            val = None
        res["secrets"][node.value] = val
        return val

    # Patches to skip functions
    for sil in SILENCE:
        PATCHES[sil] = patch(sil)

    # Patches with local mock functions
    for key, val in MOCKS.items():
        if not secrets and key == "secrets":
            continue
        # The * in the key is removed to find the mock_function (side_effect)
        # This allows us to use one side_effect to patch multiple locations
        mock_function = locals()["mock_" + key.replace("*", "")]
        PATCHES[key] = patch(val[0], side_effect=mock_function)

    # Start all patches
    for pat in PATCHES.values():
        pat.start()

    if secrets:
        # Ensure !secrets point to the patched function
        yaml_loader.yaml.SafeLoader.add_constructor("!secret",
                                                    yaml_loader.secret_yaml)

    try:
        opp = core.OpenPeerPower()
        opp.config.config_dir = config_dir

        res["components"] = opp.loop.run_until_complete(
            async_check_op_config_file(opp))
        res["secret_cache"] = OrderedDict(yaml_loader.__SECRET_CACHE)
        for err in res["components"].errors:
            domain = err.domain or ERROR_STR
            res["except"].setdefault(domain, []).append(err.message)
            if err.config:
                res["except"].setdefault(domain, []).append(err.config)

    except Exception as err:  # pylint: disable=broad-except
        _LOGGER.exception("BURB")
        print(color("red", "Fatal error while loading config:"), str(err))
        res["except"].setdefault(ERROR_STR, []).append(str(err))
    finally:
        # Stop all patches
        for pat in PATCHES.values():
            pat.stop()
        if secrets:
            # Ensure !secrets point to the original function
            yaml_loader.yaml.SafeLoader.add_constructor(
                "!secret", yaml_loader.secret_yaml)
        bootstrap.clear_secret_cache()

    return res
Ejemplo n.º 10
0
async def async_setup_opp(
    runtime_config: RuntimeConfig, ) -> core.OpenPeerPower | None:
    """Set up Open Peer Power."""
    opp = core.OpenPeerPower()
    opp.config.config_dir = runtime_config.config_dir

    async_enable_logging(
        opp,
        runtime_config.verbose,
        runtime_config.log_rotate_days,
        runtime_config.log_file,
        runtime_config.log_no_color,
    )

    opp.config.skip_pip = runtime_config.skip_pip
    if runtime_config.skip_pip:
        _LOGGER.warning(
            "Skipping pip installation of required modules. This may cause issues"
        )

    if not await conf_util.async_ensure_config_exists(opp):
        _LOGGER.error("Error getting configuration path")
        return None

    _LOGGER.info("Config directory: %s", runtime_config.config_dir)

    config_dict = None
    basic_setup_success = False
    safe_mode = runtime_config.safe_mode

    if not safe_mode:
        await opp.async_add_executor_job(conf_util.process_op_config_upgrade,
                                         opp)

        try:
            config_dict = await conf_util.async_opp_config_yaml(opp)
        except OpenPeerPowerError as err:
            _LOGGER.error(
                "Failed to parse configuration.yaml: %s. Activating safe mode",
                err,
            )
        else:
            if not is_virtual_env():
                await async_mount_local_lib_path(runtime_config.config_dir)

            basic_setup_success = (await
                                   async_from_config_dict(config_dict,
                                                          opp) is not None)

    if config_dict is None:
        safe_mode = True

    elif not basic_setup_success:
        _LOGGER.warning(
            "Unable to set up core integrations. Activating safe mode")
        safe_mode = True

    elif ("frontend" in opp.data.get(DATA_SETUP, {})
          and "frontend" not in opp.config.components):
        _LOGGER.warning(
            "Detected that frontend did not load. Activating safe mode")
        # Ask integrations to shut down. It's messy but we can't
        # do a clean stop without knowing what is broken
        with contextlib.suppress(asyncio.TimeoutError):
            async with opp.timeout.async_timeout(10):
                await opp.async_stop()

        safe_mode = True
        old_config = opp.config

        opp = core.OpenPeerPower()
        opp.config.skip_pip = old_config.skip_pip
        opp.config.internal_url = old_config.internal_url
        opp.config.external_url = old_config.external_url
        opp.config.config_dir = old_config.config_dir

    if safe_mode:
        _LOGGER.info("Starting in safe mode")
        opp.config.safe_mode = True

        http_conf = (await http.async_get_last_config(opp)) or {}

        await async_from_config_dict(
            {
                "safe_mode": {},
                "http": http_conf
            },
            opp,
        )

    if runtime_config.open_ui:
        opp.add_job(open_opp_ui, opp)

    return opp