Added custom registries for functions
This commit is contained in:
@@ -1,4 +1,8 @@
|
||||
playbook_name: "test_playbook"
|
||||
registries:
|
||||
- "./registry/directory_actions.py"
|
||||
- "./registry/borg_actions.py"
|
||||
|
||||
steps:
|
||||
- name: "backup dir1"
|
||||
actions:
|
||||
|
||||
+57
-10
@@ -1,11 +1,14 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
import yaml
|
||||
import json
|
||||
import time
|
||||
import datetime
|
||||
import importlib.util
|
||||
from os import error, name
|
||||
from typing import Callable, Dict, List, Any
|
||||
from typing import Callable, Dict, List, Any, Optional
|
||||
from dataclasses import dataclass, field
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
@@ -122,10 +125,33 @@ class StepEntry(object):
|
||||
|
||||
|
||||
class Play(object):
|
||||
def __init__(self, name : str):
|
||||
def __init__(self, name : str, registries: Optional[List[ActionRegistry]] = None):
|
||||
self.name: str = name
|
||||
self.__steps: List[StepEntry] = []
|
||||
self.__registries: List[ActionRegistry] = [registry]
|
||||
self.__registries: List[ActionRegistry] = [registry] + (registries or [])
|
||||
|
||||
@staticmethod
|
||||
def __load_registry_file(file_path: str) -> List[ActionRegistry]:
|
||||
abs_path = os.path.abspath(file_path)
|
||||
module_name = os.path.splitext(os.path.basename(abs_path))[0]
|
||||
|
||||
spec = importlib.util.spec_from_file_location(module_name, abs_path)
|
||||
if spec is None or spec.loader is None:
|
||||
raise ImportError(f"could not create spec for registry file: {file_path}")
|
||||
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules[module_name] = module
|
||||
spec.loader.exec_module(module)
|
||||
|
||||
discovered_registries = [
|
||||
obj for obj in vars(module).values()
|
||||
if isinstance(obj, ActionRegistry)
|
||||
]
|
||||
|
||||
if not discovered_registries:
|
||||
raise PlaybookError(f"No ActionRegistry instances were found in '{file_path}'")
|
||||
|
||||
return discovered_registries
|
||||
|
||||
def add_step(self, name: str, stepRunner : StepIF):
|
||||
self.__steps.append(StepEntry(name=name, step=stepRunner))
|
||||
@@ -138,8 +164,13 @@ class Play(object):
|
||||
"actions": s.step.get_action_names()
|
||||
})
|
||||
|
||||
registry_data = []
|
||||
for reg in self.__registries:
|
||||
registry_data.append(reg.manifest())
|
||||
|
||||
data = {
|
||||
"name": self.name,
|
||||
"registries": registry_data,
|
||||
"number_of_steps": len(steps_info),
|
||||
"steps": steps_info,
|
||||
}
|
||||
@@ -196,7 +227,7 @@ class Play(object):
|
||||
return CustomStep(name=name, pre_fn=pre_fn, play_fn=play_fn, post_fn=post_fn, context=ctx)
|
||||
|
||||
@classmethod
|
||||
def from_yaml(cls, fp: str) -> Play:
|
||||
def from_yaml(cls, fp: str, registries: Optional[List[ActionRegistry]] = None) -> Play:
|
||||
""" Loads the playbook from a given YAML file. """
|
||||
try:
|
||||
with open(fp, "r") as f:
|
||||
@@ -205,23 +236,39 @@ class Play(object):
|
||||
raise PlaybookError(f"Playbook file not found: {fp}") from e
|
||||
except Exception as e:
|
||||
raise PlaybookError(f"Failed to read file {fp}: {e}") from e
|
||||
|
||||
return cls.__from_yaml_str(file_contents)
|
||||
|
||||
yaml_dir = os.path.dirname(os.path.abspath(fp))
|
||||
return cls.__from_yaml_str(file_contents, base_dir=yaml_dir)
|
||||
|
||||
@classmethod
|
||||
def __from_yaml_str(cls, yaml_str: str) -> Play:
|
||||
def __from_yaml_str(cls, yaml_str: str, base_dir: str = ".") -> Play:
|
||||
data = None
|
||||
try:
|
||||
data = yaml.safe_load(yaml_str)
|
||||
except Exception as e:
|
||||
raise e
|
||||
raise PlaybookError(f"YAML Syntax error: {e}") from e
|
||||
|
||||
playbook = cls(data["playbook_name"])
|
||||
if not isinstance(data, dict) or "playbook_name" not in data:
|
||||
raise PlaybookError("YAML must contain a top-level 'playbook_name' field.")
|
||||
|
||||
# LOAD THE CUSTOM REGISTRIES
|
||||
custom_registries: List[ActionRegistry] = []
|
||||
registry_paths = data.get("registries", [])
|
||||
|
||||
for reg_path in registry_paths:
|
||||
full_path = os.path.join(base_dir, reg_path) if not os.path.isabs(reg_path) else reg_path
|
||||
try:
|
||||
regs = cls.__load_registry_file(full_path)
|
||||
custom_registries.extend(regs)
|
||||
except Exception as e:
|
||||
raise PlaybookError(f"Error loading registry '{reg_path}': {e}") from e
|
||||
|
||||
playbook = cls(data["playbook_name"], registries=custom_registries)
|
||||
|
||||
for idx, step_cfg in enumerate(data.get("steps", [])):
|
||||
step_name = step_cfg.get("name", f"step_{idx}")
|
||||
actions = step_cfg.get("actions", {})
|
||||
context = step_cfg.get("actions", {})
|
||||
context = step_cfg.get("context", {})
|
||||
|
||||
if not isinstance(actions, dict) or len(actions) != 3:
|
||||
raise PlaybookError(
|
||||
|
||||
@@ -44,3 +44,9 @@ class ActionRegistry:
|
||||
raise ValueError(f"Version '{ver}' is not registered for function '{name}'")
|
||||
|
||||
return versions_dict[ver]
|
||||
|
||||
def manifest(self):
|
||||
functions = {}
|
||||
for fn_name, versions_dict in self.__actions.items():
|
||||
functions[fn_name] = list(versions_dict.keys())
|
||||
return {"registry_name": self.name, "functions": functions}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
# borg actions registry
|
||||
from playbook.action_registry import ActionRegistry
|
||||
from playbook.models import StepLog, Status
|
||||
|
||||
borgact = ActionRegistry("borg_actions_reg")
|
||||
|
||||
@borgact.register(name="pre", version="preborg1.0")
|
||||
def pre(ctx, name) -> StepLog:
|
||||
log: StepLog = StepLog(
|
||||
step_name=name,
|
||||
status=Status.GOOD,
|
||||
msg="source directory exists",
|
||||
)
|
||||
return log
|
||||
|
||||
|
||||
@borgact.register(name="play", version="playborg1.0")
|
||||
def play(ctx, name) -> StepLog:
|
||||
log: StepLog = StepLog(
|
||||
step_name=name,
|
||||
status=Status.GOOD,
|
||||
msg="directory was copied/movied/borged/rsynced",
|
||||
)
|
||||
return log
|
||||
|
||||
|
||||
@borgact.register(name="post", version="postborg1.0")
|
||||
def post(ctx, name) -> StepLog:
|
||||
log: StepLog = StepLog(
|
||||
step_name=name,
|
||||
status=Status.GOOD,
|
||||
msg="copied/movied/borged/rsynced exists and is valid",
|
||||
)
|
||||
return log
|
||||
@@ -0,0 +1,34 @@
|
||||
# directory actions registry
|
||||
from playbook.action_registry import ActionRegistry
|
||||
from playbook.models import StepLog, Status
|
||||
|
||||
diract = ActionRegistry("directory_actions_reg")
|
||||
|
||||
@diract.register(name="directory_exists", version="stat1.0")
|
||||
def directory_exists(ctx, name) -> StepLog:
|
||||
log: StepLog = StepLog(
|
||||
step_name=name,
|
||||
status=Status.GOOD,
|
||||
msg="source directory exists",
|
||||
)
|
||||
return log
|
||||
|
||||
|
||||
@diract.register(name="copy_directory", version="cp1.0")
|
||||
def copy_directory(ctx, name) -> StepLog:
|
||||
log: StepLog = StepLog(
|
||||
step_name=name,
|
||||
status=Status.GOOD,
|
||||
msg="directory was copied/movied/borged/rsynced",
|
||||
)
|
||||
return log
|
||||
|
||||
|
||||
@diract.register(name="verify_copy", version="verify1.0")
|
||||
def verify_copy(ctx, name) -> StepLog:
|
||||
log: StepLog = StepLog(
|
||||
step_name=name,
|
||||
status=Status.GOOD,
|
||||
msg="copied/movied/borged/rsynced exists and is valid",
|
||||
)
|
||||
return log
|
||||
+30
-27
@@ -1,49 +1,52 @@
|
||||
import os
|
||||
import sys
|
||||
import json
|
||||
import argparse
|
||||
import traceback
|
||||
import importlib.util
|
||||
from typing import List
|
||||
from playbook import StepLog, CustomStep, Play, Status
|
||||
from dataclasses import asdict
|
||||
|
||||
def pre(ctx) -> StepLog:
|
||||
ctx["a"] = 5
|
||||
from playbook.action_registry import ActionRegistry
|
||||
|
||||
return StepLog(
|
||||
"pre something",
|
||||
Status.GOOD,
|
||||
0,
|
||||
msg="set 5",
|
||||
|
||||
def parse_args() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(description="Playbook Taskrunner")
|
||||
|
||||
# Flag for the playbook file
|
||||
parser.add_argument(
|
||||
"-p", "--playbook",
|
||||
type=str,
|
||||
required=True,
|
||||
help="Path to the playbook yaml file"
|
||||
)
|
||||
|
||||
def play(ctx):
|
||||
ctx["a"] = ctx["a"] + 10
|
||||
|
||||
return StepLog(
|
||||
"play something",
|
||||
Status.BAD,
|
||||
0,
|
||||
msg="added 10",
|
||||
# Flag for the custom registry files
|
||||
parser.add_argument(
|
||||
"-r", "--registry",
|
||||
nargs="+",
|
||||
default=[],
|
||||
help="One or more python files with actions"
|
||||
)
|
||||
|
||||
def post(ctx):
|
||||
ctx["a"] = ctx["a"] / 2
|
||||
return parser.parse_args()
|
||||
|
||||
return StepLog(
|
||||
"post something",
|
||||
Status.GOOD,
|
||||
0,
|
||||
msg=f"halved {ctx["a"]}",
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
args: argparse.Namespace = parse_args()
|
||||
extra_regs: List[ActionRegistry] = []
|
||||
|
||||
newPlay: Play
|
||||
try:
|
||||
newPlay = Play.from_yaml("./playbook.yaml")
|
||||
newPlay = Play.from_yaml(args.playbook, registries=extra_regs)
|
||||
except Exception as e:
|
||||
print(f"Failed to load playbook: {e}")
|
||||
traceback.print_exc()
|
||||
exit(1)
|
||||
|
||||
print(newPlay.view_playbook())
|
||||
|
||||
# print(json.dumps(asdict(newPlay.play()), indent=2))
|
||||
print(json.dumps(asdict(newPlay.play()), indent=2))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
Reference in New Issue
Block a user