%PDF- %PDF-
Mini Shell

Mini Shell

Direktori : /proc/2/cwd/opt/imunify360/venv/lib64/python3.8/site-packages/defence360agent/utils/
Upload File :
Create Path :
Current File : //proc/2/cwd/opt/imunify360/venv/lib64/python3.8/site-packages/defence360agent/utils/common.py

"""
This program is free software: you can redistribute it and/or modify it under 
the terms of the GNU General Public License as published by 
the Free Software Foundation, either version 3 of the License, 
or (at your option) any later version.


This program is distributed in the hope that it will be useful, 
but WITHOUT ANY WARRANTY; without even the implied warranty of 
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
See the GNU General Public License for more details.


You should have received a copy of the GNU General Public License
 along with this program.  If not, see <https://www.gnu.org/licenses/>.

Copyright © 2019 Cloud Linux Software Inc.

This software is also available under ImunifyAV commercial license,
see <https://www.imunify360.com/legal/eula>
"""
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


def coalesce_calls(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.

    """
    call_time = float("-inf")
    delayed_call = None

    """
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):
            nonlocal delayed_call
            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."""
                nonlocal delayed_call, call_time
                assert delayed_call
                logger.info("Schedule call %s", call_repr)
                call_time, delayed_call = loop.time(), 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 > (call_time + period):  # call immediately
                if delayed_call is not None:
                    # get string representation for logs
                    #   before cancelling the call
                    old_delayed_call_repr = str(delayed_call)
                    delayed_call.cancel()
                    old_delayed_call, delayed_call = 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,
                        call_time,
                        now,
                    )
                logger.info(
                    "Satisfy the call request soon: %s"
                    ". No calls in more than %r seconds since the start",
                    call_repr,
                    period,
                )
                delayed_call = loop.call_soon(call_delayed, coro, args, kwargs)
            elif call_time <= now <= (call_time + period):
                delay = (call_time + period) - now
                if 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 delayed_call is None
                    logger.info(
                        "Delay call request: %s for ~%.2f seconds"
                        ". Enforcing one call per %r seconds limit",
                        call_repr,
                        delay,
                        period,
                    )
                    delayed_call = loop.call_at(
                        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,
                    call_time,
                    now,
                )

        return wrapper

    return decorator


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

Zerion Mini Shell 1.0