GIF89a; Mini Shell

Mini Shell

Direktori : /opt/imunify360/venv/lib64/python3.11/site-packages/defence360agent/utils/
Upload File :
Current File : //opt/imunify360/venv/lib64/python3.11/site-packages/defence360agent/utils/common.py

import asyncio
import datetime
import functools
import logging
import socket
import time

MINUTE = datetime.timedelta(minutes=1).total_seconds()
HOUR = datetime.timedelta(hours=1).total_seconds()
DAY = datetime.timedelta(days=1).total_seconds()
WEEK = datetime.timedelta(weeks=1).total_seconds()

logger = logging.getLogger(__name__)


class ServiceBase(object):
    """Base service class."""

    def __init__(self, loop):
        self._loop = loop
        self._should_stop = False
        self._main_task = None
        self._state = self.StoppedState(self)

    def start(self):
        return self._state.start()

    def should_stop(self):
        return self._state.should_stop()

    async def wait(self):
        return await self._state.wait()

    def is_running(self):
        return self._state.is_running()

    async def _run(self):
        raise NotImplementedError

    class State(object):
        def __init__(self, obj):
            """:type obj: ServiceBase"""
            self._obj = obj

        def start(self):
            pass

        def should_stop(self):
            pass

        async def wait(self):
            task = self._obj._main_task
            if task:
                await task

        def is_running(self):
            return False

    class StoppedState(State):
        def _on_stop(self, future):
            self._obj._state = ServiceBase.StoppedState(self._obj)
            self._obj._should_stop = False

        def start(self):
            obj = self._obj
            obj._main_task = obj._loop.create_task(obj._run())
            obj._main_task.add_done_callback(self._on_stop)
            obj._state = ServiceBase.RunningState(obj)

    class RunningState(State):
        def should_stop(self):
            obj = self._obj
            obj._should_stop = True
            obj._main_task.cancel()
            obj._state = ServiceBase.StoppingState(obj)

        def is_running(self):
            return True

    class StoppingState(State):
        def start(self):
            raise ProgrammingError(
                "Cannot start stopping service. Please wait while it stop."
            )


class ProgrammingError(Exception):
    pass


class RateLimit:
    """Decorator to limit function calls to one per *period* seconds.

    If less than *period* seconds have passed since the last call,
    then the request to call the function is replace with an *on_drop*
    call with the same arguments.

    If *on_drop* is None [default] then the call is just dropped

    """

    def __init__(self, period, timer=time.monotonic, *, on_drop=None):
        self._next_call_time = None
        self._period = period
        self._timer = timer
        self._on_drop = on_drop

    @property
    def should_be_called(self):
        return (
            self._next_call_time is None
            or self._next_call_time <= self._timer()
        )

    def __call__(self, func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            if self.should_be_called:
                self._next_call_time = self._timer() + self._period
                return func(*args, **kwargs)
            elif self._on_drop is not None:
                return self._on_drop(*args, **kwargs)

        @functools.wraps(func)
        async def async_wrapper(*args, **kwargs):
            if self.should_be_called:
                self._next_call_time = self._timer() + self._period
                return await func(*args, **kwargs)
            elif self._on_drop is not None:
                return self._on_drop(*args, **kwargs)

        return async_wrapper if asyncio.iscoroutinefunction(func) else wrapper


rate_limit = RateLimit


class CoalesceCalls:
    def __init__(self):
        self.call_time = float("-inf")
        self.delayed_call = None

    def coalesce_calls(self, period, *, done_callback=None):
        """
        Decorator to coalesce coroutine calls to one per *period* seconds.

        Requests for a coroutine call in a given time period are coalesced:
        If t is the time of the last call, then N call requests in the [t,
        t+period) time interval results in a single call at the
        t+period time iff N>0 i.e.,

        if less than *period* seconds have passed since the last call,
        then the calls are coalesced: (N-1) requests are dropped, Nth
        requests is performed in *period* seconds.

        It is unspecified which exact call is made if arguments differ.

        If the call is not dropped then *done_callback* is attached
        to the task when the coroutine is scheduled with the event loop.

        Given `c` is the time of the last [actual] call (`loop.create_task()`)
        And `T` is the coalesce time period
        When a call request arrives at `t` time
        Then
        | call pending?  | t>c+T                          | c<=t<=c+T  | t<c  |
        |----------------+--------------------------------+------------+------|
        | no p. call     | call soon                      | call at c+T| warn |
        | p. call at c+T | cancel the call/warn, call soon| drop call  | warn |
        """

        def decorator(coro):
            @functools.wraps(coro)
            async def wrapper(*args, **kwargs):
                loop = kwargs.get("loop")
                if loop is None:
                    loop = asyncio.get_event_loop()

                if args or kwargs:
                    args_repr = "*%r, **%r" % (args, kwargs)
                else:  # special case no args case
                    args_repr = ""
                call_repr = "%s(%s)" % (coro.__name__, args_repr)

                def log_exception(task):
                    """Log task's error
                       if any with event's loop exception handler.

                    CancelledError is not logged.
                    """
                    if not task.cancelled() and task.exception() is not None:
                        loop.call_exception_handler(
                            {
                                "message": "Unhandled exception during "
                                + call_repr,
                                "exception": task.exception(),
                                "task": task,
                            }
                        )

                def call_delayed(coro, args, kwargs):
                    """Call & schedule the delayed coroutine now."""
                    logger.info("Schedule call %s", call_repr)
                    self.call_time = loop.time()
                    self.delayed_call = None
                    task = loop.create_task(coro(*args, **kwargs))
                    task.add_done_callback(
                        log_exception
                        if done_callback is None
                        else done_callback
                    )

                now = loop.time()
                if now > (self.call_time + period):  # call immediately
                    if self.delayed_call is not None:
                        # get string representation for logs
                        #   before cancelling the call
                        old_delayed_call_repr = str(self.delayed_call)
                        self.delayed_call.cancel()
                        self.delayed_call = None
                        logger.warning(
                            "There was a scheduled call (%s)"
                            " but more than period (%r) seconds passed"
                            " since the last call (%r, now=%r)",
                            old_delayed_call_repr,
                            period,
                            self.call_time,
                            now,
                        )
                    logger.info(
                        "Satisfy the call request soon: %s. No calls in"
                        " more than %r seconds since the start",
                        call_repr,
                        period,
                    )
                    self.delayed_call = loop.call_soon(
                        call_delayed, coro, args, kwargs
                    )
                elif self.call_time <= now <= (self.call_time + period):
                    delay = (self.call_time + period) - now
                    if self.delayed_call is not None:  # drop call request
                        logger.info(
                            "Drop call request for %s"
                            ", enforcing one call per %r seconds limit"
                            ". Next call is in ~%.2f seconds",
                            call_repr,
                            period,
                            delay,
                        )
                    else:  # schedule call request
                        assert self.delayed_call is None
                        logger.info(
                            "Delay call request: %s for ~%.2f seconds"
                            ". Enforcing one call per %r seconds limit",
                            call_repr,
                            delay,
                            period,
                        )
                        self.delayed_call = loop.call_at(
                            self.call_time + period,
                            call_delayed,
                            coro,
                            args,
                            kwargs,
                        )
                else:  # now < call_time
                    logger.warning(
                        "Drop call request for %s, reason: last call time"
                        " (%r, now=%r) is in the future",
                        call_repr,
                        self.call_time,
                        now,
                    )

            return wrapper

        return decorator


webserver_gracefull_restart = CoalesceCalls()


def get_hostname():
    """Returns readable name of the server.

    It is sent to CLN and allows user to sort out his servers.
    """
    hostname = socket.getfqdn()
    if hostname is None or hostname.lower().startswith("localhost"):
        return socket.gethostname()
    return hostname

./BlackJoker Mini Shell 1.0