Skip to content

Commit

Permalink
Added support for storage backend and event handler plugins
Browse files Browse the repository at this point in the history
Signed-off-by: Christian Pinto <christian.pinto@ibm.com>
  • Loading branch information
christian-pinto committed Jun 25, 2024
1 parent 49d8c33 commit c1d1a8c
Show file tree
Hide file tree
Showing 11 changed files with 104 additions and 17 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,8 @@ tests/.pytest_cache
# Remove directory __pycache__
__pycache__
venv

.idea
build
sunfish.egg-info

7 changes: 6 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@ description = ""
authors = ["Erika Rosaverde <erika.rosaverde@ibm.com>"]
readme = "README.md"
packages = [
{ include = "sunfish" }
{ include = "sunfish" },
{ include = "sunfish_plugins", from = "." }
]

[tool.setuptools.packages.find]
where = ["."]
include = ["sunfish_plugins.*"]

[tool.poetry.dependencies]
python = ">=3.9"
flask = "^2.3.3"
Expand Down
64 changes: 54 additions & 10 deletions sunfish/lib/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,14 @@
import logging
from typing import Optional

from sunfish.storage.backend_FS import BackendFS
from sunfish.lib.exceptions import CollectionNotSupported, ResourceNotFound, AgentForwardingFailure, PropertyNotFound
from sunfish.events.redfish_event_handler import RedfishEventHandler, RedfishEventHandlersTable
from sunfish_plugins.event_handlers.redfish.redfish_event_handler import RedfishEventHandler

from sunfish.events.redfish_subscription_handler import RedfishSubscriptionHandler
from sunfish.lib.object_handler import RedfishObjectHandler
from sunfish.models.types import *
from sunfish.lib.agents_management import Agent

import sunfish.models.plugins as plugin_modules
logger = logging.getLogger(__name__)


Expand All @@ -31,17 +30,62 @@ def __init__(self, conf):

self.conf = conf

if conf['storage_backend'] == 'FS':
self.storage_backend = BackendFS(self.conf)
# elif conf['storage_backend'] == '<OTHER STORAGE>':
# ...
# The Sunfish core library uses a plugin mechanism that allows dynamic loading of certain classes. This helps
# users with updating the behavior of the sunfish library without having to modify its core classes.
# At the moment we support plugins for the storage backend and for the redfish event handlers.
# Plugins are implemented as namespaced packages and must be placed in a folder at the top of the project named
# "sunfish_plugins", with subfolders named "storage" and/or "event_handlers". The python packages defined inside
# each subfolder are totally user defined.
# ── sunfish_plugins
# ├── storage
# │ └──my_storage_package <--- User defined
# │ ├── __init__.py
# │ └── my_storage_backend.py
# └── event_handlers
# └──my_handler_package <--- User defined
# ├── __init__.py
# └── my_handler.py

# When initializing the Sunfish libraries can load their storage or event handler plugin by specifying them in
# the configuration as in the below example:
#
# "storage_backend" : {
# "module_name": "storage.my_storage_package.my_storage_backend",
# "class_name": "StorageBackend"
# },
# "event_backend" : {
# "module_name": "event-handlers.my_handler_package.my_handler",
# "class_name": "StorageBackend"
# },
#
# In both cases "class_name" represents the name of the class that is initialized and implements the respective
# interface.

# Default storage plugin loaded if nothing is specified in the configuration
if not "storage_backend" in conf:
storage_plugin = {
"module_name": "storage.file_system_backend.backend_FS",
"class_name": "BackendFS"
}
else:
storage_plugin = conf["storage_backend"]
storage_cl = plugin_modules.load_plugin(storage_plugin)
self.storage_backend = storage_cl(self.conf)

# Default event_handler plugin loaded if nothing is specified in the configuration
if not "event_handler" in conf:
event_plugin = {
"module_name": "event_handlers.redfish.redfish_event_handler",
"class_name": "RedfishEventHandler"
}
else:
event_plugin = conf["event_handler"]
event_cl = plugin_modules.load_plugin(event_plugin)
self.event_handler = event_cl(self)

if conf['handlers']['subscription_handler'] == 'redfish':
self.subscription_handler = RedfishSubscriptionHandler(self)

if conf['handlers']['event_handler'] == 'redfish':
self.event_handler = RedfishEventHandler(self)

def get_object(self, path: string):
"""Calls the correspondent read function from the backend implementation and checks that the path is valid.
Expand Down
19 changes: 19 additions & 0 deletions sunfish/models/plugins.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright IBM Corp. 2024
# This software is available to you under a BSD 3-Clause License.
# The full license terms are available here: https://github.com/OpenFabrics/sunfish_library_reference/blob/main/LICENSE

import importlib
import logging

logger = logging.getLogger(__name__)

plugins_namespace_name = "sunfish_plugins"


def load_plugin(plugin: dict):
module_name = f"{plugins_namespace_name}.{plugin['module_name']}"
logger.info(f"Loading plugin {module_name}...")
plugin_module = importlib.import_module(module_name)
logger.info("Plugin loaded")
return getattr(plugin_module, plugin["class_name"])

Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import json
import logging
import os
from uuid import uuid4
import warnings

import requests
Expand Down
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import shutil

from sunfish.storage.backend_interface import BackendInterface
from sunfish.storage import utils
from sunfish_plugins.storage.file_system_backend import utils
from sunfish.lib.exceptions import *

logger = logging.getLogger(__name__)
Expand Down
File renamed without changes.
9 changes: 8 additions & 1 deletion tests/conf.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
{
"storage_backend": "FS",
"redfish_root": "/redfish/v1/",
"storage_backend": {
"module_name": "storage.file_system_backend.backend_FS",
"class_name": "BackendFS"
},
"backend_conf" : {
"fs_root": "Resources",
"subscribers_root": "EventService/Subscriptions"
},
"event_handler": {
"module_name": "event_handlers.redfish.redfish_event_handler",
"class_name": "RedfishEventHandler"
},
"handlers": {
"subscription_handler": "redfish",
"event_handler": "redfish"
Expand Down
14 changes: 11 additions & 3 deletions tests/test_sunfishcore_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,12 @@
# from http.server import BaseHTTPRequestHandler
import json
import os
import shutil
import logging
import pytest
import requests
from pytest_httpserver import HTTPServer
from sunfish.lib.core import Core
from sunfish.lib.exceptions import *
from tests import test_utils, tests_template

class TestSunfishcoreLibrary():
@classmethod
def setup_class(cls):
Expand All @@ -27,6 +24,17 @@ def setup_class(cls):

cls.core = Core(cls.conf)

@pytest.mark.order("first")
def test_init_core(self):
path = os.path.join(os.getcwd(), 'tests', 'conf.json')
try:
json_data = open(path)
conf = json.load(json_data)
except FileNotFoundError as e:
raise ResourceNotFound('conf.json')

core = Core(conf)

# TEST REST
# Delete
@pytest.mark.order("last")
Expand Down

0 comments on commit c1d1a8c

Please sign in to comment.