140 lines
4.6 KiB
Python
140 lines
4.6 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Copyafter 2020 Red Hat
|
|
# GNU General Public License v3.0+
|
|
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
|
|
from __future__ import absolute_import, division, print_function
|
|
|
|
__metaclass__ = type
|
|
|
|
import re
|
|
from importlib import import_module
|
|
from ansible.plugins.action import ActionBase
|
|
from ansible.module_utils._text import to_native
|
|
from ansible_collections.ansible.utils.plugins.modules.fact_diff import (
|
|
DOCUMENTATION,
|
|
)
|
|
from ansible_collections.ansible.utils.plugins.module_utils.common.argspec_validate import (
|
|
AnsibleArgSpecValidator,
|
|
)
|
|
|
|
|
|
class ActionModule(ActionBase):
|
|
"""action module"""
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(ActionModule, self).__init__(*args, **kwargs)
|
|
self._supports_async = True
|
|
self._task_vars = None
|
|
|
|
def _check_argspec(self):
|
|
aav = AnsibleArgSpecValidator(
|
|
data=self._task.args,
|
|
schema=DOCUMENTATION,
|
|
schema_format="doc",
|
|
name=self._task.action,
|
|
)
|
|
valid, errors, self._task.args = aav.validate()
|
|
if not valid:
|
|
self._result["failed"] = True
|
|
self._result["msg"] = errors
|
|
|
|
def _debug(self, msg):
|
|
"""Output text using ansible's display
|
|
|
|
:param msg: The message
|
|
:type msg: str
|
|
"""
|
|
msg = "<{phost}> [fact_diff][{plugin}] {msg}".format(
|
|
phost=self._playhost, plugin=self._plugin, msg=msg
|
|
)
|
|
self._display.vvvv(msg)
|
|
|
|
def _load_plugin(self, plugin, directory, class_name):
|
|
"""Load a plugin from the fs
|
|
|
|
:param plugin: The name of the plugin in collection format
|
|
:type plugin: string
|
|
:param directory: The name of the plugin directory to use
|
|
:type directory: string
|
|
:param class_name: The name of the class to load from the plugin
|
|
:type class_name: string
|
|
:return: An instance of class class_name
|
|
:rtype: class_name
|
|
"""
|
|
if len(plugin.split(".")) != 3:
|
|
msg = "Plugin name should be provided as a full name including collection"
|
|
self._result["failed"] = True
|
|
self._result["msg"] = msg
|
|
return None
|
|
cref = dict(zip(["corg", "cname", "plugin"], plugin.split(".")))
|
|
cref.update(directory=directory)
|
|
parserlib = "ansible_collections.{corg}.{cname}.plugins.{directory}.{plugin}".format(
|
|
**cref
|
|
)
|
|
try:
|
|
class_obj = getattr(import_module(parserlib), class_name)
|
|
class_instance = class_obj(
|
|
task_args=self._task.args,
|
|
task_vars=self._task_vars,
|
|
debug=self._debug,
|
|
)
|
|
return class_instance
|
|
except Exception as exc:
|
|
self._result["failed"] = True
|
|
self._result[
|
|
"msg"
|
|
] = "Error loading plugin '{plugin}': {err}".format(
|
|
plugin=plugin, err=to_native(exc)
|
|
)
|
|
return None
|
|
|
|
def _run_diff(self, plugin_instance):
|
|
try:
|
|
result = plugin_instance.diff()
|
|
if "errors" in result:
|
|
self._result["failed"] = True
|
|
self._result["msg"] = result["errors"]
|
|
return result
|
|
|
|
except Exception as exc:
|
|
msg = "Unhandled exception from plugin '{plugin}'. Error: {err}".format(
|
|
plugin=self._task.args["plugin"]["name"], err=to_native(exc)
|
|
)
|
|
self._result["failed"] = True
|
|
self._result["msg"] = msg
|
|
return None
|
|
|
|
def run(self, tmp=None, task_vars=None):
|
|
self._task.diff = True
|
|
self._result = super(ActionModule, self).run(tmp, task_vars)
|
|
self._task_vars = task_vars
|
|
self._playhost = task_vars.get("inventory_hostname")
|
|
|
|
self._check_argspec()
|
|
if self._result.get("failed"):
|
|
return self._result
|
|
|
|
self._plugin = self._task.args["plugin"]["name"]
|
|
plugin_instance = self._load_plugin(
|
|
self._plugin, "fact_diff", "FactDiff"
|
|
)
|
|
if self._result.get("failed"):
|
|
return self._result
|
|
|
|
result = self._run_diff(plugin_instance)
|
|
if self._result.get("failed"):
|
|
return self._result
|
|
|
|
ansi_escape = re.compile(r"\x1B[@-_][0-?]*[ -/]*[@-~]")
|
|
diff_text = ansi_escape.sub("", result["diff"])
|
|
self._result.update(
|
|
{
|
|
"diff": {"prepared": result["diff"]},
|
|
"changed": bool(result["diff"]),
|
|
"diff_lines": diff_text.splitlines(),
|
|
"diff_text": diff_text,
|
|
}
|
|
)
|
|
return self._result
|