1
0
Fork 0

updates the Module classes with documentation and deduplicated some code

Dieser Commit ist enthalten in:
Sebastian Tobie 2023-04-15 09:40:27 +02:00
Ursprung aa0723809a
Commit e3a4c894ea
1 geänderte Dateien mit 54 neuen und 32 gelöschten Zeilen

Datei anzeigen

@ -1,5 +1,5 @@
import pathlib import pathlib
from typing import Any, Callable, ClassVar, Dict, Optional, TypeVar from typing import Any, Callable, ClassVar, Dict, Optional, TypeVar, NoReturn, overload
import ansible.module_utils.basic as basic import ansible.module_utils.basic as basic
from ansible.module_utils.generic import _sdict from ansible.module_utils.generic import _sdict
@ -14,17 +14,24 @@ T = TypeVar("T")
class AnsibleModule(object): class AnsibleModule(object):
"""Simple wrapper for the mo""" """Simple wrapper for the basic.AnsibleModule"""
#: name of the module. This is required for the generation of the Ansible documentation
name: ClassVar[str] name: ClassVar[str]
#: The AnsibleModule for this Module
module: basic.AnsibleModule module: basic.AnsibleModule
#: TODO
msg: str msg: str
#: The result of this Module call. It always contains the changed key, so in any case an Module can report if it changed anything
result: dict result: dict
#: the specification of the arguments. Subclasses that are usable Modules must set this value.
module_spec: ClassVar[dict] module_spec: ClassVar[dict]
#: This is set by classes that define common things for their subclasses, like behaviour of the run and check methods. This is used by `SystemdUnitModule`
_common_args = dict() _common_args = dict()
@property @property
def params(self) -> Dict[str, Any]: def params(self) -> Dict[str, Any]:
"""params is an wrapper for the module.params"""
return self.module.params # type: ignore return self.module.params # type: ignore
def __init__(self): def __init__(self):
@ -37,28 +44,40 @@ class AnsibleModule(object):
self.tmpdir = pathlib.Path(self.module.tmpdir) self.tmpdir = pathlib.Path(self.module.tmpdir)
def set(self, key: str, value): def set(self, key: str, value):
"""sets an value for the result"""
self.result[key] = value self.result[key] = value
@overload
def diff(self, diff: Dict[str, str]):
pass
def diff( def diff(
self, self,diff: Optional[Dict[str, str]] = None,*,
before, before: Optional[str] = None,
after, after: Optional[str] = None,
before_header: Optional[str] = None, before_header: Optional[str] = None,
after_header: Optional[str] = None, after_header: Optional[str] = None,
): ):
"""adds the special return value "diff". This allows Modules to present the changes of files to the caller. it takes care of the special semantics of the return value"""
if "diff" not in self.result: if "diff" not in self.result:
self.result["diff"] = list() self.result["diff"] = list()
diff = dict( if diff is not None and not any((before is not None, after is not None)):
before=before, pass
after=after, elif all((before is not None, after is not None, diff is None)):
) diff = dict(
if before_header is not None: before=before,
diff["before_header"] = before_header after=after,
if after_header is not None: )
diff["after_header"] = after_header if before_header is not None:
diff["before_header"] = before_header
if after_header is not None:
diff["after_header"] = after_header
else:
raise TypeError("only diff or before and after can be set, not both of them")
self.result["diff"].append(diff) self.result["diff"].append(diff)
def get(self, key: str, default: T = None) -> T: def get(self, key: str, default: T = None) -> T:
"""returns an Parameter of the Module."""
if key not in self.params.keys(): if key not in self.params.keys():
return default return default
if self.params[key] is None and default is not None: if self.params[key] is None and default is not None:
@ -69,10 +88,12 @@ class AnsibleModule(object):
@property @property
def changed(self): def changed(self):
"""returns if changes were detected/made"""
return self.result["changed"] return self.result["changed"]
@changed.setter @changed.setter
def changed_set(self, value): def changed_set(self, value):
"""sets the changed value. this is always converted to bool"""
self.result["changed"] = not not value self.result["changed"] = not not value
def prepare(self): def prepare(self):
@ -84,7 +105,9 @@ class AnsibleModule(object):
def run(self): def run(self):
raise NotImplementedError() raise NotImplementedError()
def __call__(self): def __call__(self) -> NoReturn:
"""This calls the module. first prepare is called and then check or run, depending on the check mode.
If an exception is raised this is catched and the module automatically fails with an traceback"""
self.prepare() self.prepare()
try: try:
if self.module.check_mode: if self.module.check_mode:
@ -102,6 +125,7 @@ class AnsibleModule(object):
@classmethod @classmethod
def doc(cls) -> str: def doc(cls) -> str:
"""this returns the documentation string of the module. If the help arguments of an Types method was given, it adds this as an helptext of this parameter"""
try: try:
import yaml import yaml
except ImportError: except ImportError:
@ -145,11 +169,14 @@ class AnsibleModule(object):
class SystemdUnitModule(AnsibleModule): class SystemdUnitModule(AnsibleModule):
#: path of the unitfile managed by this module
unitfile: pathlib.Path unitfile: pathlib.Path
#: subclasses of this always support the file common args and the check mode
_common_args = dict( _common_args = dict(
supports_check_mode=True, supports_check_mode=True,
add_file_common_args=True, add_file_common_args=True,
) )
#: if defined it will be called after run has changed the unitfile
post: Optional[Callable[[], None]] post: Optional[Callable[[], None]]
def unit(self) -> str: def unit(self) -> str:
@ -163,55 +190,50 @@ class SystemdUnitModule(AnsibleModule):
self.module.set_group_if_different(path.as_posix(), "root", False) self.module.set_group_if_different(path.as_posix(), "root", False)
self.module.set_mode_if_different(path.as_posix(), "0644", False) self.module.set_mode_if_different(path.as_posix(), "0644", False)
if self.unitfile.exists(): if self.unitfile.exists():
if "diff" not in self.result:
self.result["diff"] = list()
diff = dict() diff = dict()
self.result["changed"] = self.module.set_owner_if_different( self.changed = self.changed | self.module.set_owner_if_different(
self.unitfile.as_posix(), self.unitfile.as_posix(),
"root", "root",
self.result["changed"], self.result["changed"],
diff, diff,
) )
self.result["changed"] = self.module.set_group_if_different( self.diff(diff)
diff = dict()
self.changed = self.changed | self.module.set_group_if_different(
self.unitfile.as_posix(), self.unitfile.as_posix(),
"root", "root",
self.result["changed"], self.result["changed"],
diff, diff,
) )
self.result["changed"] = self.module.set_mode_if_different( self.diff(diff)
diff = dict()
self.changed = self.changed | self.module.set_mode_if_different(
self.unitfile.as_posix(), self.unitfile.as_posix(),
"0644", "0644",
self.result["changed"], self.result["changed"],
diff, diff,
) )
self.result["diff"].append(diff) self.diff(diff)
def check(self): def check(self):
if "changed" in self.result:
changed = self.result["changed"]
else:
changed = False
self.unitfile_gen() self.unitfile_gen()
if not self.unitfile.exists(): if not self.unitfile.exists():
self.diff("", self.unit(), self.unitfile.as_posix()) self.diff(before="", after=self.unit(), before_header=self.unitfile.as_posix())
changed = True self.changed = True
else: else:
if self.module.sha256(self.unitfile.as_posix()) != self.module.sha256( if self.module.sha256(self.unitfile.as_posix()) != self.module.sha256(
(self.tmpdir / "newunit").as_posix() (self.tmpdir / "newunit").as_posix()
): ):
changed = True self.changed = True
self.diff( self.diff(
before=self.unitfile.read_text(), before=self.unitfile.read_text(),
after=self.unit(), after=self.unit(),
before_header=self.unitfile.as_posix(), before_header=self.unitfile.as_posix(),
) )
self.set("changed", changed)
if hasattr(self, "post") and self.post is not None:
self.post()
return changed
def run(self): def run(self):
if not self.check(): self.check()
if not self.changed:
return return
self.module.atomic_move( self.module.atomic_move(
src=(self.tmpdir / "newunit").as_posix(), src=(self.tmpdir / "newunit").as_posix(),