Plugins

The service supports the system of plugins. Plugins must be written in the Python programming language.

Plugin types

There are three sorts of plugins:

  • On event plugin. The plugin is triggered when an event occurs. The plugin should implement a callback function. This function is called on each event of the corresponding type. The set of event types is defined by the service developers.

    Supported event types:

    event type

    description

    monitoring_event

    Event contains monitoring points for sending to a custom monitoring system

    Plugin example:

    """
    Module request monitoring plugin example
    """
    
    import asyncio
    from abc import abstractmethod
    from typing import TypeVar
    
    from aiohttp import ClientSession
    from luna_plugins.base.plugins_meta.base_plugins import BaseEventPlugin
    
    MonitoringPoint = TypeVar("MonitoringPoint")
    LunaApplication = TypeVar("LunaApplication")
    
    
    class BaseRequestMonitoringPlugin(BaseEventPlugin):
        """
        Base class for requests monitoring.
        """
    
        # event name for triggering callback
        eventName = "monitoring_event"
    
        @abstractmethod
        async def flushPointToMonitoring(self, point: "MonitoringPoint", logger) -> None:
            """
            All plugins must realize this method.
    
            This function call after end of request
    
            Args:
                point: point for monitoring
                logger: logger
            """
    
        async def handleEvent(self, points: list["MonitoringPoint"], logger):
            await asyncio.gather(*[self.flushPointToMonitoring(point, logger) for point in points])
    
    
    class RequestMonitoringPlugin(BaseRequestMonitoringPlugin):
        """
        Example plugin sends a request data for monitoring to third-party source.
        Only one instance of this class exist during the program execution.
        """
    
        def __init__(self, app: "LunaApplication"):
            super().__init__(app)
            self.url = "http://127.0.0.1:5020/1/buckets"
            self.session: ClientSession | None = None
            self.bucket = "plugin_test_bucket"
    
        async def close(self):
            """
            Stop plugin.
    
            Close all open connections and ect
            """
            if self.session:
                await self.session.close()
    
        async def initialize(self):
            """
            Initialize plugin.
    
            Close all open connections and ect
            """
            self.session = ClientSession()
            async with self.session.post(f"{self.url}?bucket={self.bucket}") as resp:
                if resp.status not in (201, 409):
                    response = await resp.json()
                    raise RuntimeError(f"failed create bucket, {self.bucket}, response: {response}")
    
        async def flushPointToMonitoring(self, point: "MonitoringPoint", logger) -> None:
            """
            Callback for sending a request monitoring data.
    
            Args:
                point: point for monitoring
                logger: logger
            """
            logger.debug(f"Plugin 'flushPointToMonitoring' get point, series: {point.series}, time: {point.eventTime}")
            msg = {"tags": point.tags, "fields": point.fields}
            async with self.session.post(f"{self.url}/{self.bucket}/objects", json=msg) as resp:
                logger.info(resp.status)
                logger.info(await resp.text())
    

    This plugin demonstrates the sending of a request monitoring data to another service. All monitoring plugins must implement the BaseRequestMonitoringPlugin abstract class.

  • Background plugin. This sort of plugin is intended for background work.

    The background plugin can implement:

    • custom route

    • background monitoring of service resources

    • collaboration of an event plugin and a background plugin (batching monitoring points)

    • connection to other data sources (Redis, RabbitMQ) and their data processing

    Plugin example:

    """
    Module realizes background plugin example
    """
    
    import asyncio
    from asyncio import Task
    
    from luna_plugins.base.plugins_meta.base_plugins import BaseBackgroundHandler, BaseBackgroundPlugin, pluginHTTPResponse
    
    
    class HandlerExample(BaseBackgroundHandler):
        """
        Handler example
        """
    
        async def get(self, request):  # pylint: disable=unused-argument
            """
            Method get example.
    
            Returns:
                response
            """
            return self.response(body="I am teapot", headers={"Content-Type": "text/plain"}, statusCode=418)
    
    
    def anotherHandlerExample(request):  # pylint: disable=unused-argument
        """Standalone handler example"""
        return pluginHTTPResponse(statusCode=200, body="T800", headers={"Content-Type": "text/plain"})
    
    
    class BackgroundPluginExample(BaseBackgroundPlugin):
        """
        Background plugin example.
    
        Create background task and add a route.
        """
    
        def __init__(self, app: "LunaApplication"):
            super().__init__(app)
            self.task: Task | None = None
            self.temperature = 0
    
        async def initialize(self):
            """
            Initialize plugin
            """
            self.addRoute("/teapot", HandlerExample)
            self.addRoute("/teapot/version", anotherHandlerExample, methods={"get"})
    
        async def close(self):
            """
            Stop background process
            Returns:
    
            """
            if self.task:
                self.task.cancel()
    
        async def usefulJob(self):
            """
            Some useful  async work
            """
            while True:
                await asyncio.sleep(1)
                self.temperature = min(100, self.temperature + 1)
                if self.temperature < 100:
                    self.app.ctx.logger.info(f"I boil water, temperature: {self.temperature}")
                else:
                    self.app.ctx.logger.info("boiling water is ready, would you care for a cup of tea?")
    
        async def start(self):
            """
            Run background process
    
            .. warning::
                The function suppose that the process is handle in this coroutine. The coroutine must start
                the process only without awaiting a end of the process
            """
            self.task = asyncio.create_task(self.usefulJob())
    

    This plugin demonstrates background work and implements a route. All background plugins must implement the BaseRequestMonitoringPlugin abstract class.

  • Builtin plugin. This sort of plugin is provided only by VL and allow to get access to external VL services which not included to luna-platform. Each plugin provides access to part or whole resources of external service.

    At this moment available plugins are:
    • luna-streams

    The plugin also check authorization parameters for access to several resources. It is possible to use Luna-Account-Id/Basic/Bearer authorization to make requests just like for all other endpoint of luna-api.

    It is also possible to delimit access permissions using token permission. To make it possible, it needs to create new or update existing token passing custom token permission keys, for example {“permissions”: {“streams”: [“creation”, “view”, “modification”, “deletion”], “groups”: [“create”, “view”]}}

    These parameters will works the same way as for all other luna-api resources.

    Luna-Streams builtin plugin

    Requirements:
    • properly executed Luna-Streams service

    • properly specified plugin settings. For example:

      "LUNA_API_PLUGINS_SETTINGS": {
              "luna-streams": {
                  "luna-streams-address": {"origin": "http://127.0.0.1:5160", "api_version": 1},
                  "luna-streams-timeouts": {"request": 42, "connect": 42}
              }
          }
      }
      

    * prefix is /6/plugins/luna-streams

    luna-api route

    luna-streams route

    description

    permission key

    prefix/version

    /version

    get luna-streams api version

    prefix/docs/spec

    /{api_version}/docs/spec

    get luna-streams spec documentation

    prefix/streams

    /{api_version}/streams

    create new/get/delete existing streams

    streams

    prefix/streams/count

    /{api_version}/streams/count

    get streams count

    streams

    prefix/streams/<stream-id>

    /{api_version}/streams/<stream-id>

    replace/get/delete stream

    streams

    prefix/plugins

    /{api_version}/plugins

    get list of active plugins

    prefix/streams/logs

    /{api_version}/streams/logs

    get/delete streams logs

    streams

    prefix/streams/<stream-id> /preview/handler/live

    /{api_version}/streams/<stream-id> /preview/handler/live

    get stream live preview

    streams streams

    prefix/streams/<stream-id> /preview/handler/frame

    /{api_version}/streams/logs /preview/handler/frame

    get stream frame preview

    streams streams

    prefix/groups

    /{api_version}/groups

    create new/get existing groups

    groups

    prefix/groups/count

    /{api_version}/groups/count

    get groups count

    groups

    prefix/groups/<group-id>

    /{api_version}/groups/<group-id>

    update/get/delete group

    groups

    prefix/linker

    /{api_version}/linker

    link/unlink stream(s) to/from group(s)

    groups

Note

It is allowed to make requests to Luna-Streams v1 or v2 api version using LUNA-STREAMS-API-VERSION header:

  • LUNA-STREAMS-API-VERSION header is not specified, the 1 luna-streams api_version will be selected

  • LUNA-STREAMS-API-VERSION header value is 1, the 1 luna-streams api_version will be selected

  • LUNA-STREAMS-API-VERSION header value is 2, the 2 luna-streams api_version will be selected

  • otherwise, an error will be received as respone

Enable plugin

If the user implements a plugin, the file with the plugin should be added to the luna_api/plugins directory of the service. The plugin filename should be added to the LUNA_API_ACTIVE_PLUGINS configuration setting.