+102
-74
@@ -1,13 +1,13 @@
|
||||
from __future__ import annotations
|
||||
from re import A
|
||||
|
||||
from dataclasses import dataclass
|
||||
import yaml
|
||||
from abc import ABC, abstractmethod
|
||||
from pydantic import BaseModel
|
||||
|
||||
from playbook.models import ActModel, PlaybookModel, timed_run
|
||||
from playbook.models import ActModel, PlaybookModel, StepModel, timed_run
|
||||
from playbook.logging_models import Status, StepLogModel
|
||||
from playbook.action_registry import ActionFn
|
||||
|
||||
from playbook.action_registry import ActionFn, ActionRegistry
|
||||
|
||||
|
||||
class StepIF(ABC):
|
||||
@@ -21,18 +21,6 @@ class StepIF(ABC):
|
||||
""" Return a mapping of action roles (pre, play, post) to function names. """
|
||||
pass
|
||||
|
||||
|
||||
# class PlaybookError(Exception):
|
||||
# """ Base exception for Playbook parsing and execution failures. """
|
||||
# pass
|
||||
|
||||
|
||||
# @dataclass
|
||||
# class StepEntry(object):
|
||||
# name: str
|
||||
# step: StepIF
|
||||
|
||||
|
||||
# class Play(object):
|
||||
# def __init__(self, name: str, registries: dict[str, ActionRegistry]):
|
||||
# self.name: str = name
|
||||
@@ -130,76 +118,116 @@ class StepIF(ABC):
|
||||
# playLog.msg = "success"
|
||||
#
|
||||
# return playLog
|
||||
#
|
||||
# def _play_steps(self, steps: list[CustomStep]) -> StepLogModel:
|
||||
# playLog: StepLogModel = StepLogModel(
|
||||
# step_name=self.name,
|
||||
# status=Status.GOOD,
|
||||
# duration_sec=0,
|
||||
# msg="",
|
||||
# error=[],
|
||||
# substeps=[],
|
||||
# )
|
||||
#
|
||||
# prev_ctx: dict[str, object] = {}
|
||||
# for step in steps:
|
||||
# log: StepLogModel = timed_run(step.run, self._context | prev_ctx)
|
||||
# prev_ctx = log.pipe_ctx
|
||||
#
|
||||
# playLog.substeps.append(log)
|
||||
#
|
||||
# if log.failed:
|
||||
# playLog.error.append({
|
||||
# "status": "failed",
|
||||
# "output": f"failed in step: {step.name}"
|
||||
# })
|
||||
# playLog.status = Status.BAD
|
||||
# break
|
||||
#
|
||||
# if playLog.status == Status.GOOD:
|
||||
# playLog.msg = "success"
|
||||
#
|
||||
# return playLog
|
||||
#
|
||||
# def _get_function_from_registry(self, fn_name) -> ActionFn:
|
||||
# for reg in self._registries.values():
|
||||
# try:
|
||||
# return reg.get(fn_name, "*")
|
||||
# except ValueError:
|
||||
# continue
|
||||
#
|
||||
# raise ValueError(f"no function with name '{fn_name}' was found")
|
||||
#
|
||||
# def _build_custom_step(self, name, actions, ctx) -> CustomStep:
|
||||
# functionlist: list[ActionFn] = []
|
||||
# for f in actions:
|
||||
# try:
|
||||
# fn = self._get_function_from_registry(actions[f])
|
||||
# functionlist.append(fn)
|
||||
# except KeyError as e:
|
||||
# raise PlaybookError(
|
||||
# f"Step '{name}' is missing required action field: {e}") from e
|
||||
#
|
||||
# return CustomStep(name, functionlist, ctx)
|
||||
|
||||
|
||||
class PlaybookYAMLParser():
|
||||
@staticmethod
|
||||
def _parse_registries(reg_names: list[str]) -> list[ActionRegistry]:
|
||||
registries: list[ActionRegistry] = []
|
||||
for reg in reg_names:
|
||||
registries.extend(ActionRegistry.load_registries_from_file(reg))
|
||||
return registries
|
||||
|
||||
@staticmethod
|
||||
def _parse_steps(steps: list[dict[str, object]],
|
||||
registries: list[ActionRegistry]) -> list[StepModel]:
|
||||
step_list: list[StepModel] = []
|
||||
for step in steps:
|
||||
if not isinstance(step['name'], str):
|
||||
raise ValueError(
|
||||
f"name must of type `str` not `{type(step["name"])}`")
|
||||
if not isinstance(step['action'], str):
|
||||
raise ValueError(
|
||||
f"action must of type `str` not `{type(step["action"])}`")
|
||||
if not isinstance(step['context'], dict):
|
||||
raise ValueError(
|
||||
f"context must of type `dict` not `{type(step["context"])}`")
|
||||
|
||||
name: str = step['name']
|
||||
action: str = step['action']
|
||||
context: dict[str, object] = step['context']
|
||||
|
||||
found = False
|
||||
fn: ActionFn
|
||||
for reg in registries:
|
||||
try:
|
||||
fn = reg.get(action)
|
||||
found = True
|
||||
except:
|
||||
continue
|
||||
|
||||
if not found:
|
||||
raise ValueError(
|
||||
f"Unable to find function `{action}` in any registry")
|
||||
|
||||
step_list.append(StepModel(name=name, action=fn, context=context))
|
||||
|
||||
return step_list
|
||||
|
||||
@staticmethod
|
||||
def _parse_acts(acts_data: list[dict[str, object]],
|
||||
registries: list[ActionRegistry]) -> list[ActModel]:
|
||||
acts: list[ActModel] = []
|
||||
for act in acts_data:
|
||||
if not isinstance(act["name"], str):
|
||||
raise ValueError(
|
||||
f"name must of type `str` not `{type(act["name"])}`")
|
||||
|
||||
if not isinstance(act["steps"], list):
|
||||
raise ValueError(
|
||||
f"steps must of type `list` not `{type(act["steps"])}`")
|
||||
|
||||
name: str = act["name"]
|
||||
steps: list[dict[str, object]] = act["steps"]
|
||||
|
||||
step_list = PlaybookYAMLParser._parse_steps(steps, registries)
|
||||
modeled_act: ActModel = ActModel(name=name, steps=step_list)
|
||||
acts.append(modeled_act)
|
||||
|
||||
return acts
|
||||
|
||||
@staticmethod
|
||||
def from_yaml(fp: str) -> Playbook:
|
||||
with open(fp, "rb") as source_file:
|
||||
data = yaml.safe_load(source_file)
|
||||
|
||||
reg_yaml = data.get("registries", [])
|
||||
registries = PlaybookYAMLParser._parse_registries(reg_yaml)
|
||||
|
||||
acts_yaml = data.get("acts", {})
|
||||
acts: list[ActModel] = PlaybookYAMLParser._parse_acts(
|
||||
acts_yaml, registries)
|
||||
|
||||
global_context = data.get("global_context", {})
|
||||
|
||||
log_dir: str = data.get("log_dir", "./")
|
||||
playbook_name: str = data.get("playbook_name")
|
||||
|
||||
new_playbook: PlaybookModel = PlaybookModel(
|
||||
playbook_name=playbook_name,
|
||||
acts=acts,
|
||||
log_dir=log_dir,
|
||||
registries=registries,
|
||||
global_context=global_context
|
||||
)
|
||||
|
||||
return Playbook(new_playbook)
|
||||
pass
|
||||
|
||||
|
||||
class Playbook():
|
||||
model: PlaybookModel
|
||||
|
||||
def __init__(self, model: PlaybookModel):
|
||||
self.model = model
|
||||
self.model = model
|
||||
|
||||
@property
|
||||
def acts(self) -> list[ActModel]:
|
||||
return self.model.acts
|
||||
|
||||
|
||||
@classmethod
|
||||
def from_yaml(cls, fp: str) -> Playbook:
|
||||
try:
|
||||
new_playboook = PlaybookModel.from_yaml_file(fp)
|
||||
return Playbook(new_playboook)
|
||||
except Exception as e:
|
||||
raise e
|
||||
return PlaybookYAMLParser.from_yaml(fp)
|
||||
|
||||
def view_playbook(self) -> str:
|
||||
return self.model.model_dump_json()
|
||||
|
||||
Reference in New Issue
Block a user