kheops/ansible_tree/managers.py

438 lines
13 KiB
Python

import copy
import json
import textwrap
from prettytable import PrettyTable
from pathlib import Path
# from box import Box
from jsonmerge import Merger
import re
import logging
from pprint import pprint
import collections
from ansible_tree.utils import schema_validate, str_ellipsis
import ansible_tree.plugin as TreePlugins
log = logging.getLogger(__name__)
# DEPRECATED class BackendEngineLoader():
# DEPRECATED
# DEPRECATED def get_class(self, item):
# DEPRECATED engine_name = item.get('engine')
# DEPRECATED assert (isinstance(engine_name, str)), f"Got: {engine_name} for {item}"
# DEPRECATED
# DEPRECATED # Check engine presence
# DEPRECATED if not hasattr(TreePlugins.engine, engine_name):
# DEPRECATED raise Exception(f"No plugin {engine_name} found for entry {item}!")
# DEPRECATED
# DEPRECATED cls = getattr(TreePlugins.engine, engine_name).Plugin
# DEPRECATED return cls
# def BackendPluginInit(backends, ctx):
#
# for be in backends:
# be.run = {}
# be.scope = ctx['scope']
# be.key = ctx['key']
#
# return backends
# def BackendPluginHier(backends, ctx):
#
# new_backends = []
# for cand in backends:
#
# # Init
# plugin_config = cand.config.get("hierarchy", None)
# hier_data = plugin_config.get("data", None)
# if not hier_data:
# new_backends.append(cand)
# continue
#
# # Retrieve config data
# hier_var = plugin_config.get("var", "item")
# hier_sep = plugin_config.get("separator", "/")
# if isinstance(hier_data, str):
# hier_data = cand.scope.get(hier_data, None)
#
# # Build a new list
#
# pprint (plugin_config)
# pprint (hier_data)
#
# if isinstance(hier_data, str):
# r = hier_data.split(hier_sep)
# assert (isinstance(r, list)), f"Got: {r}"
#
# ret1 = []
# for index, part in enumerate(r):
#
# try:
# prefix = ret1[index - 1]
# except IndexError:
# prefix = f'{hier_sep}'
# prefix = ""
# item = f"{prefix}{part}{hier_sep}"
# ret1.append(item)
#
# ret2 = []
# for item in ret1:
# _cand = copy.deepcopy(cand)
# run = {
# "index": index,
# "hier_value": item,
# "hier_var": hier_var,
# }
# _cand.run['hier'] = run
# _cand.scope[hier_var] = item
# ret2.append(_cand)
# print ("RESULT")
# pprint (ret2)
#
# new_backends.extend(ret2)
# return new_backends
#
#
#def BackendPluginLoop(backends, ctx):
#
# new_backends = []
# for cand in backends:
#
# # Init
# loop_config = cand.config.get("loop", None)
# loop_data = loop_config.get("data", None)
# if not loop_data:
# new_backends.append(cand)
# continue
#
# # Retrieve config data
# loop_var = loop_config.get("var", "item")
# if isinstance(loop_data, str):
# loop_data = cand.scope.get(loop_data, None)
# assert (isinstance(loop_data, list)), f"Got: {loop_data}"
#
# # Build a new list
# ret = []
# for idx, item in enumerate(loop_data):
# _cand = copy.deepcopy(cand)
# run = {
# "loop_index": idx,
# "loop_value": item,
# "loop_var": loop_var,
# }
# _cand.run['loop'] = run
# _cand.scope[loop_var] = item
# ret.append(_cand)
#
# new_backends.extend(ret)
#
# return new_backends
class LoadPlugin():
def __init__(self, plugins):
self.plugins = plugins
def load(self, kind, name):
assert (isinstance(name, str)), f"Got: {name}"
# Get plugin kind
try:
plugins = getattr(self.plugins, kind)
except Exception as e:
raise Exception(f"Unknown module kind '{kind}': {e}")
# Get plugin class
try:
plugin_cls = getattr(plugins, name)
except Exception as e:
raise Exception(f"Unknown module '{kind}.{name}': {e}")
assert (hasattr(plugin_cls, 'Plugin')), f'Plugin {kind}/{name} is not a valid plugin'
# Return plugin Classe
return plugin_cls.Plugin
class BackendsManager():
_schema_props_default = {
"$schema": 'http://json-schema.org/draft-04/schema#',
"default": "",
"oneOf": [
{
"type": "string",
"default": "BLAAAAHHH"
},
{
"type": "object",
"additionalProperties": True,
"default": {},
"properties": {
"engine": {
"type": "string",
"default": "jerakia",
"optional": False,
},
"value": {
"default": 'UNSET',
"optional": False,
},
},
},
]
}
def _validate_item(self, item):
if isinstance(item, str):
item = {
"engine": self.config_main.default_engine,
"value": item,
}
item = schema_validate(item, self._schema_props_default)
assert (isinstance(item, dict))
return item
def __init__(self, app):
self.app = app
self.config_app = app.conf2['config']['app']
self.config_main = app.conf2['config']['tree']
self.config_items = list(app.conf2['tree'])
# THIS MAKE A BUG !!!! self.plugin_loader = LoadPlugin(TreePlugins)
self.plugins = [
'init',
'loop',
'hier',
]
# Auto init
self.backends = self.config_items
def query(self, key=None, scope=None, trace=False):
backends = self.get_backends(key=key, scope=scope, trace=trace)
ret = self.get_results(backends, trace=trace)
return ret
def get_backends(self, key=None, scope=None, trace=False):
log.debug(f"Look for candidates for key '{key}' in backend: {self.backends}")
# Prepare plugins
plugin_loader = LoadPlugin(TreePlugins)
_run = {
"key": key,
"scope": scope,
}
# Preprocess backends plugins
backends = self.config_items
log.debug(f"Backend preprocessing of {len(backends)} elements")
for plugin in self.plugins:
#backend_cls = plugin_loader.load('backend', plugin)
plugin = plugin_loader.load(
'backend', plugin
)()
log.debug(f"Run {plugin}")
new_backend, _run = plugin.process(backends, _run)
assert(isinstance(new_backend, list)), f"Got: {new_backend}"
assert(isinstance(_run, dict)), f"Got: {_run}"
backends = new_backend
# pprint (backends)
for i in backends:
assert (i.get('engine')), f"Got: {i}"
log.debug(f"Backend preprocessing made {len(backends)} elements")
return backends
def get_results(self, backends, trace=False):
# Prepare plugins
plugin_loader = LoadPlugin(TreePlugins)
new_results = []
for backend in backends:
#result_cls = result_loader.load('result', result)
# print ("BACKKENNDNNDNDNDND")
# pprint(backend)
engine = plugin_loader.load(
'engine', backend['engine']
)(
backend,
parent=self, app=self.app)
log.debug(f"Run engine: {engine}")
new_result = engine.process()
assert(isinstance(new_result, list)), f"Got: {new_result}"
new_results.extend(new_result)
# Filter out? Not here !new_results = [i for i in new_results if i['found'] ]
# pprint (new_results)
# print ("OKKKKKKKKKKKKKKKKKKKKKKKKK SO FAR")
return new_results
class RulesManager():
default_merge_schema = {
"$schema": 'http://json-schema.org/draft-04/schema#',
"oneOf": [
{
"type": "array",
"mergeStrategy": "append",
# "mergeStrategy": "arrayMergeById",
},
{
"type": "object",
"mergeStrategy": "objectMerge",
},
{
"type": "boolean",
"mergeStrategy": "overwrite",
},
{
"type": "string",
"mergeStrategy": "overwrite",
},
{
"type": "integer",
"mergeStrategy": "overwrite",
},
{
"type": "number",
"mergeStrategy": "overwrite",
},
{
"type": "null",
"mergeStrategy": "overwrite",
},
],
}
rule_schema = {
"$schema": 'http://json-schema.org/draft-04/schema#',
"type": "object",
"additionalProperties": False,
"properties": {
"rule": {
"default": ".*",
"optional": True,
"oneOf": [
{
"type": "string",
},
{
"type": "null",
},
],
},
"trace": {
"type": "boolean",
"default": False,
},
"explain": {
"type": "boolean",
"default": False,
},
"strategy": {
"type": "string",
"default": "schema",
# "default": "last",
"optional": True,
# "enum": ["first", "last", "merge"],
},
"schema": {
"type": "object",
"default": None,
"optional": True,
"oneOf": [
{
"type": "string",
},
{
"type": "null",
},
{
"type": "object",
},
],
},
}
}
def __init__(self, app):
self.app = app
self.config_app = app.conf2['config']['app']
self.config_main = app.conf2['config']['rules']
self.config_items = list(app.conf2['rules'])
def get_result(self, candidates, key=None, scope=None, trace=False, explain=False):
#trace=False
rules = self.config_items
key = key or ''
# Filter out invalid candidates
matched_candidates = [i for i in candidates if i['found'] == True]
if len(matched_candidates) == 0:
log.debug("No matched candidates")
return None
# Look for matching key in rules defiunitions
regex_support = False
matched_rule = {}
if regex_support:
raise Exception("Not Implemented")
else:
rule = [ i for i in rules if i.get('rule') == key ]
if len(rule) == 0:
log.debug(f"No matched rule for {key}, applying defaults")
else:
matched_rule = rule[0]
log.debug(f"Matcher rule for {key}: {matched_rule}")
matched_rule['trace'] = trace
matched_rule['explain'] = explain
schema_validate(matched_rule, self.rule_schema)
# Prepare plugins
assert(isinstance(matched_candidates, list)), f"Got: {matched_candidates}"
assert(isinstance(matched_rule, dict)), f"Got: {matched_rule}"
strategy = matched_rule.get('strategy', 'first')
log.debug(f"Key '{key}' matched rule '{rule}' with '{strategy}' strategy")
# Load plugin
log.debug(f"Run strategy: {strategy}")
plugin_loader = LoadPlugin(TreePlugins)
strategy = plugin_loader.load('strategy',
strategy,
)(parent=self, app=self.app)
new_result = strategy.process(matched_candidates, matched_rule)
return new_result