Commits vergleichen
46 Commits
Autor | SHA1 | Datum | |
---|---|---|---|
56c3c3a745 | |||
d2d0b7dc44 | |||
73b050d9b1 | |||
7c24aee1c7 | |||
73889fe8ef | |||
31b5fa26a8 | |||
531d8fdb0e | |||
aa53ad470a | |||
8ee18934a9 | |||
3d528fe1a6 | |||
548dacd614 | |||
1755385a6b | |||
d64aeaa4fa | |||
8efa463b3d | |||
fe4064b3c3 | |||
f61e735a41 | |||
e1b9a7928e | |||
872e1c1883 | |||
f843ffdef3 | |||
dd8493d059 | |||
3198a0d064 | |||
022b5d3ee5 | |||
9e05b08500 | |||
2e74882b7b | |||
0ae59dc167 | |||
c0dfa0a951 | |||
eec5816319 | |||
2671cb7401 | |||
855b1c0466 | |||
283e551204 | |||
affe76fbcf | |||
4242ce0c95 | |||
920da46658 | |||
1c3d89e961 | |||
e845a06d09 | |||
48872edc14 | |||
9e2b4eef8b | |||
5feb959f42 | |||
62e4cbe040 | |||
dcc685616b | |||
70e0146d02 | |||
23d1c655f4 | |||
de7292f3e6 | |||
3e0cb17222 | |||
20bb6b1f16 | |||
c3404200a9 |
25 geänderte Dateien mit 1075 neuen und 809 gelöschten Zeilen
|
@ -1,10 +1,98 @@
|
|||
=====================================
|
||||
sebastian.systemd 0.3.0 Release Notes
|
||||
sebastian.systemd 0.4.3 Release Notes
|
||||
=====================================
|
||||
|
||||
.. contents:: Topics
|
||||
|
||||
|
||||
v0.4.3
|
||||
======
|
||||
|
||||
Changelog
|
||||
---------
|
||||
|
||||
added new options to system_service
|
||||
|
||||
v0.4.2
|
||||
======
|
||||
|
||||
Changelog
|
||||
---------
|
||||
|
||||
upgraded to the new method used by sebastian.base
|
||||
|
||||
v0.4.0
|
||||
======
|
||||
|
||||
Changelog
|
||||
---------
|
||||
|
||||
added timer module
|
||||
|
||||
v0.3.8
|
||||
======
|
||||
|
||||
Changelog
|
||||
---------
|
||||
|
||||
fixed the import again.
|
||||
|
||||
v0.3.7
|
||||
======
|
||||
|
||||
Changelog
|
||||
---------
|
||||
|
||||
fixed the import path
|
||||
|
||||
v0.3.6
|
||||
======
|
||||
|
||||
Changelog
|
||||
---------
|
||||
|
||||
fixed the import of the module utils
|
||||
|
||||
v0.3.5
|
||||
======
|
||||
|
||||
Changelog
|
||||
---------
|
||||
|
||||
added some ignores and changed my name
|
||||
|
||||
v0.3.4
|
||||
======
|
||||
|
||||
Changelog
|
||||
---------
|
||||
|
||||
moved the module_utils to an indipendent repository
|
||||
|
||||
v0.3.3
|
||||
======
|
||||
|
||||
Changelog
|
||||
---------
|
||||
|
||||
added netdev module
|
||||
|
||||
v0.3.2
|
||||
======
|
||||
|
||||
Changelog
|
||||
---------
|
||||
|
||||
added virtualization and negative matches to link and network
|
||||
|
||||
v0.3.1
|
||||
======
|
||||
|
||||
Changelog
|
||||
---------
|
||||
|
||||
fixed runtime.yml
|
||||
|
||||
v0.3.0
|
||||
======
|
||||
|
||||
|
|
15
Makefile
Normale Datei
15
Makefile
Normale Datei
|
@ -0,0 +1,15 @@
|
|||
format:
|
||||
black .
|
||||
isort .
|
||||
|
||||
changelog:
|
||||
antsibull-changelog generate
|
||||
|
||||
docs: format
|
||||
update-doc
|
||||
|
||||
release: changelog docs
|
||||
ansible-galaxy collection build --output-path dist
|
||||
|
||||
upload: release
|
||||
./upload.sh
|
|
@ -1,7 +0,0 @@
|
|||
changelog:
|
||||
antsibull-changelog generate
|
||||
docs:
|
||||
./update_doc
|
||||
|
||||
release: changelog docs
|
||||
ansible-galaxy collection build
|
|
@ -2,41 +2,75 @@ ancestor: null
|
|||
releases:
|
||||
0.1.0:
|
||||
changes:
|
||||
release_date: '2023-04-15'
|
||||
release_summary: Erstes Release
|
||||
major_changes:
|
||||
- added systemd_link module
|
||||
- added systemd_mount module
|
||||
- added systemd_network module
|
||||
modules:
|
||||
- name: systemd_link
|
||||
description: Erstellt eine Systemd Link Unit
|
||||
- name: systemd_mount
|
||||
description: Erstellt eine Systemd mount Unit
|
||||
- name: systemd_network
|
||||
description: Erstellt eine Systemd Network Unit
|
||||
release_summary: Erstes Release
|
||||
release_date: "2023-04-15"
|
||||
0.1.1:
|
||||
changes:
|
||||
release_date: '2023-04-15'
|
||||
release_summary: kleines feature update
|
||||
minor_changes:
|
||||
- update_doc hinzugefügt. um Dokumentation automatisch zu aktualisieren
|
||||
- "update_doc hinzugef\xFCgt. um Dokumentation automatisch zu aktualisieren"
|
||||
release_summary: kleines feature update
|
||||
release_date: "2023-04-15"
|
||||
0.1.2:
|
||||
changes:
|
||||
release_date: '2023-04-15'
|
||||
release_summary: Pfad der auflösung für Tests und autodoc geändert, weil Mitogen sonst blockiert
|
||||
release_summary: "Pfad der aufl\xF6sung f\xFCr Tests und autodoc ge\xE4ndert, weil Mitogen sonst blockiert"
|
||||
release_date: "2023-04-15"
|
||||
0.1.3:
|
||||
changes:
|
||||
release_date: "2023-04-20"
|
||||
release_summary: "renamed all modules to names without systemd_ prefix"
|
||||
release_summary: renamed all modules to names without systemd_ prefix
|
||||
release_date: "2023-04-20"
|
||||
0.2.0:
|
||||
changes:
|
||||
release_date: "2023-04-21"
|
||||
release_summary: "added an rudimentary system_service module"
|
||||
modules:
|
||||
- name: system_service
|
||||
description: erstellt ein einfachen systemd system service
|
||||
release_summary: added an rudimentary system_service module
|
||||
release_date: "2023-04-21"
|
||||
0.3.0:
|
||||
changes:
|
||||
release_date: "2023-04-23"
|
||||
release_summary: "added socket module and some small fixes"
|
||||
release_summary: added socket module and some small fixes
|
||||
release_date: "2023-04-23"
|
||||
0.3.1:
|
||||
changes:
|
||||
release_summary: fixed runtime.yml
|
||||
release_date: "2023-04-23"
|
||||
0.3.2:
|
||||
changes:
|
||||
release_summary: added virtualization and negative matches to link and network
|
||||
release_date: "2023-07-15"
|
||||
0.3.3:
|
||||
changes:
|
||||
release_summary: added netdev module
|
||||
release_date: "2023-07-16"
|
||||
0.3.4:
|
||||
changes:
|
||||
release_summary: moved the module_utils to an indipendent repository
|
||||
release_date: "2023-11-26"
|
||||
0.3.5:
|
||||
changes:
|
||||
release_summary: added some ignores and changed my name
|
||||
release_date: "2023-12-30"
|
||||
0.3.6:
|
||||
changes:
|
||||
release_summary: fixed the import of the module utils
|
||||
release_date: "2024-02-11"
|
||||
0.3.7:
|
||||
changes:
|
||||
release_summary: fixed the import path
|
||||
release_date: "2024-02-11"
|
||||
0.3.8:
|
||||
changes:
|
||||
release_summary: fixed the import again.
|
||||
release_date: "2024-02-11"
|
||||
0.4.0:
|
||||
release_date: "2024-02-24"
|
||||
changes:
|
||||
release_summary: added timer module
|
||||
0.4.2:
|
||||
release_date: "2024-03-09"
|
||||
changes:
|
||||
release_summary: upgraded to the new method used by sebastian.base
|
||||
0.4.3:
|
||||
release_date: "2024-03-09"
|
||||
changes:
|
||||
release_summary: added new options to system_service
|
||||
|
|
24
galaxy.yml
24
galaxy.yml
|
@ -1,21 +1,31 @@
|
|||
---
|
||||
namespace: sebastian
|
||||
name: systemd
|
||||
version: 0.3.0
|
||||
version: 0.4.4
|
||||
|
||||
readme: README.md
|
||||
|
||||
authors:
|
||||
- Sebastian Tobie
|
||||
description: An simple for generating systemd units with ansible
|
||||
license_file: 'LICENSE'
|
||||
license_file: "LICENSE"
|
||||
tags:
|
||||
- systemd
|
||||
- linux
|
||||
dependencies: {}
|
||||
repository: https://gitea.sebastian-tobie.de/sebastian/ansible-systemd.git
|
||||
dependencies:
|
||||
sebastian.base: ">=0.5.0"
|
||||
repository: https://gitea.sebastian-tobie.de/ansible/ansible-systemd.git
|
||||
# documentation: http://docs.example.com
|
||||
homepage: https://gitea.sebastian-tobie.de/sebastian/ansible-systemd
|
||||
issues: https://gitea.sebastian-tobie.de/sebastian/ansible-systemd/issues
|
||||
build_ignore: []
|
||||
homepage: https://gitea.sebastian-tobie.de/ansible/ansible-systemd
|
||||
issues: https://gitea.sebastian-tobie.de/ansible/ansible-systemd/issues
|
||||
build_ignore:
|
||||
- "*.gz"
|
||||
- ".*"
|
||||
- Makefile
|
||||
- pyproject.toml
|
||||
- upload.sh
|
||||
- htmlcov
|
||||
- changelogs
|
||||
- docs
|
||||
- plugins/modules/unit.py.example
|
||||
# manifest: null
|
||||
|
|
|
@ -1,52 +1,4 @@
|
|||
---
|
||||
# Collections must specify a minimum required ansible version to upload
|
||||
# to galaxy
|
||||
# requires_ansible: '>=2.9.10'
|
||||
|
||||
# Content that Ansible needs to load from another location or that has
|
||||
# been deprecated/removed
|
||||
# plugin_routing:
|
||||
# action:
|
||||
# redirected_plugin_name:
|
||||
# redirect: ns.col.new_location
|
||||
# deprecated_plugin_name:
|
||||
# deprecation:
|
||||
# removal_version: "4.0.0"
|
||||
# warning_text: |
|
||||
# See the porting guide on how to update your playbook to
|
||||
# use ns.col.another_plugin instead.
|
||||
# removed_plugin_name:
|
||||
# tombstone:
|
||||
# removal_version: "2.0.0"
|
||||
# warning_text: |
|
||||
# See the porting guide on how to update your playbook to
|
||||
# use ns.col.another_plugin instead.
|
||||
# become:
|
||||
# cache:
|
||||
# callback:
|
||||
# cliconf:
|
||||
# connection:
|
||||
# doc_fragments:
|
||||
# filter:
|
||||
# httpapi:
|
||||
# inventory:
|
||||
# lookup:
|
||||
# module_utils:
|
||||
# modules:
|
||||
# netconf:
|
||||
# shell:
|
||||
# strategy:
|
||||
# terminal:
|
||||
# test:
|
||||
# vars:
|
||||
|
||||
# Python import statements that Ansible needs to load from another location
|
||||
# import_redirection:
|
||||
# ansible_collections.ns.col.plugins.module_utils.old_location:
|
||||
# redirect: ansible_collections.ns.col.plugins.module_utils.new_location
|
||||
|
||||
# Groups of actions/modules that take a common set of options
|
||||
# action_groups:
|
||||
# group_name:
|
||||
# - module1
|
||||
# - module2
|
||||
requires_ansible: '>=2.9.10'
|
||||
|
|
|
@ -1,118 +0,0 @@
|
|||
import pathlib
|
||||
import warnings
|
||||
from typing import Any, Callable, Dict, Optional, Sequence, Tuple, Type, Union
|
||||
|
||||
__all__ = (
|
||||
"Types",
|
||||
"SYSTEMD_SERVICE_CONFIG",
|
||||
"SYSTEMD_NETWORK_CONFIG",
|
||||
"SYSTEMD_CONFIG_ROOT",
|
||||
"systemdbool",
|
||||
"AnsibleParameter",
|
||||
)
|
||||
|
||||
|
||||
SYSTEMD_CONFIG_ROOT = pathlib.Path("/etc/systemd")
|
||||
SYSTEMD_NETWORK_CONFIG = SYSTEMD_CONFIG_ROOT / "network"
|
||||
SYSTEMD_SERVICE_CONFIG = SYSTEMD_CONFIG_ROOT / "system"
|
||||
|
||||
AnsibleParameter = Dict[str, Any]
|
||||
|
||||
|
||||
class _Type:
|
||||
def __dir__(self) -> tuple:
|
||||
return (
|
||||
"str",
|
||||
"bool",
|
||||
"int",
|
||||
"float",
|
||||
"path",
|
||||
"raw",
|
||||
"jsonarg",
|
||||
"json",
|
||||
"bytes",
|
||||
"dict",
|
||||
"list",
|
||||
"bits",
|
||||
"__doc__",
|
||||
)
|
||||
|
||||
def list(
|
||||
self,
|
||||
elements: Union[Type[object], str, AnsibleParameter],
|
||||
required: bool = False,
|
||||
help: Optional[str] = None,
|
||||
) -> AnsibleParameter:
|
||||
option: AnsibleParameter = dict(type="list", required=required)
|
||||
if not isinstance(elements, (str, dict)):
|
||||
option["elements"] = elements.__name__
|
||||
elif isinstance(elements, dict):
|
||||
option["elements"] = elements["type"]
|
||||
if elements["type"] == "dict":
|
||||
option["options"] = dict()
|
||||
for name, value in elements["option"].items():
|
||||
option["options"][name] = value
|
||||
if "description" not in option["options"][name]:
|
||||
warnings.warn(
|
||||
"helptext of option {} is unset. Ansible requires suboptions to have an documentation".format(name),
|
||||
)
|
||||
if help is not None:
|
||||
option["description"] = help.split("\n")
|
||||
return option
|
||||
|
||||
def dict(self, required: bool = False, help: Optional[str] = None, **options: dict) -> AnsibleParameter:
|
||||
option: AnsibleParameter = dict(type="dict", required=required)
|
||||
option["option"] = options
|
||||
if help is not None:
|
||||
option["description"] = help.split("\n")
|
||||
return option
|
||||
|
||||
def __getattr__(self, name: str):
|
||||
def argument(
|
||||
required: bool = False,
|
||||
help: Optional[str] = None,
|
||||
choices: Optional[Sequence] = None,
|
||||
default: Optional[Any] = None,
|
||||
) -> AnsibleParameter:
|
||||
"""Simple wrapper for Ansible {0} argument dict"""
|
||||
option: AnsibleParameter = dict(type=name, required=required)
|
||||
if choices is not None:
|
||||
option["choices"] = choices
|
||||
if default is not None:
|
||||
option["default"] = default
|
||||
if help is not None:
|
||||
option["description"] = help.split("\n")
|
||||
return option
|
||||
|
||||
argument.__name__ = name
|
||||
argument.__doc__ = argument.__doc__.format(name) # type: ignore
|
||||
return argument
|
||||
|
||||
|
||||
Types = _Type()
|
||||
|
||||
|
||||
def systemdbool(b: Union[bool, str]) -> str:
|
||||
if b is True:
|
||||
return "yes"
|
||||
elif b is False:
|
||||
return "no"
|
||||
return b
|
||||
|
||||
|
||||
def modspec(
|
||||
argument_spec: Dict[str, Dict[str, Any]],
|
||||
mutually_exclusive: Sequence[Tuple[str, ...]] = (),
|
||||
required_together: Sequence[Tuple[str, ...]] = (),
|
||||
required_one_of: Sequence[Tuple[str, ...]] = (),
|
||||
required_if: Sequence[Union[Tuple[str, Any, Tuple[str, ...]], Tuple[str, Any, Tuple[str, ...], bool]]] = (),
|
||||
required_by: Dict[str, Union[str, Tuple[str, ...]]] = {},
|
||||
) -> Dict[str, Any]:
|
||||
return dict(
|
||||
argument_spec=argument_spec,
|
||||
mutually_exclusive=mutually_exclusive,
|
||||
required_together=required_together,
|
||||
required_one_of=required_one_of,
|
||||
required_if=required_if,
|
||||
required_by=required_by,
|
||||
)
|
|
@ -1,360 +0,0 @@
|
|||
import pathlib
|
||||
from copy import deepcopy
|
||||
from typing import Any, Callable, ClassVar, Dict, NoReturn, Optional, Type, TypeVar, Union, overload
|
||||
|
||||
import ansible.module_utils.basic as basic
|
||||
|
||||
try:
|
||||
from ansible_collections.sebastian.systemd.plugins.module_utils.generic import AnsibleParameter, Types
|
||||
except ImportError:
|
||||
from plugins.module_utils.generic import AnsibleParameter, Types
|
||||
|
||||
|
||||
__all__ = (
|
||||
"AnsibleModule",
|
||||
"SystemdUnitModule",
|
||||
"installable",
|
||||
"SystemdReloadMixin",
|
||||
)
|
||||
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
def docify(input: Union[dict, AnsibleParameter]) -> dict:
|
||||
options = dict()
|
||||
for name, help in input.items():
|
||||
options[name] = dict(type=help["type"])
|
||||
if "description" in help:
|
||||
if isinstance(help["description"], str):
|
||||
help["description"] = help["description"].split("\n")
|
||||
options[name]["description"] = help["description"]
|
||||
if "required" in help and help["required"]:
|
||||
options[name]["required"] = True
|
||||
else:
|
||||
options[name]["required"] = False
|
||||
if help["type"] == "list":
|
||||
options[name]["elements"] = help["elements"]
|
||||
if not options[name]["required"]:
|
||||
options[name]["default"] = []
|
||||
if "default" in help:
|
||||
options[name]["default"] = help["default"]
|
||||
if "options" in help:
|
||||
options[name]["options"] = docify(help["options"])
|
||||
if "choices" in help:
|
||||
options[name]["choices"] = tuple(help["choices"])
|
||||
return options
|
||||
|
||||
|
||||
class AnsibleModule(object):
|
||||
"""Simple wrapper for the basic.AnsibleModule"""
|
||||
|
||||
#: name of the module. This is required for the generation of the Ansible documentation
|
||||
name: ClassVar[str]
|
||||
#: The AnsibleModule for this Module
|
||||
module: basic.AnsibleModule
|
||||
#: 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
|
||||
#: the specification of the arguments. Subclasses that are usable Modules must set this value.
|
||||
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()
|
||||
|
||||
@property
|
||||
def params(self) -> Dict[str, Any]:
|
||||
"""params is an wrapper for the module.params"""
|
||||
return self.module.params # type: ignore
|
||||
|
||||
def __init__(self):
|
||||
self.result = dict(changed=False)
|
||||
specs = dict()
|
||||
specs.update(deepcopy(self._common_args))
|
||||
modspec = self.module_spec
|
||||
if "argument_spec" in modspec and "argument_spec" in self._common_args:
|
||||
specs["argument_spec"].update(modspec["argument_spec"])
|
||||
del modspec["argument_spec"]
|
||||
specs.update(modspec)
|
||||
self.module = basic.AnsibleModule(**specs)
|
||||
self.tmpdir = pathlib.Path(self.module.tmpdir)
|
||||
|
||||
def set(self, key: str, value):
|
||||
"""sets an value for the result"""
|
||||
self.result[key] = value
|
||||
|
||||
@overload
|
||||
def diff(self, diff: Dict[str, str]):
|
||||
pass
|
||||
|
||||
@overload
|
||||
def diff(
|
||||
self,
|
||||
before: Optional[str] = None,
|
||||
after: Optional[str] = None,
|
||||
before_header: Optional[str] = None,
|
||||
after_header: Optional[str] = None,
|
||||
):
|
||||
pass
|
||||
|
||||
def diff( # type: ignore
|
||||
self,
|
||||
diff=None,
|
||||
*,
|
||||
before=None,
|
||||
after=None,
|
||||
before_header=None,
|
||||
after_header=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:
|
||||
self.result["diff"] = list()
|
||||
if diff is not None and not any((before is not None, after is not None)):
|
||||
pass
|
||||
elif all((before is not None, after is not None, diff is None)):
|
||||
diff = dict(
|
||||
before=before,
|
||||
after=after,
|
||||
)
|
||||
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)
|
||||
|
||||
def get(self, key: str, default: T = None) -> T:
|
||||
"""returns an Parameter of the Module."""
|
||||
if self.params[key] is None and default is not None:
|
||||
return default
|
||||
elif self.params[key] is None and default is None:
|
||||
raise KeyError()
|
||||
return self.params[key]
|
||||
|
||||
def changed_get(self):
|
||||
"""value that shows if changes were detected/made"""
|
||||
return self.result["changed"]
|
||||
|
||||
def changed_set(self, value):
|
||||
self.result["changed"] = not not value
|
||||
|
||||
changed = property(changed_get, changed_set, None, changed_get.__doc__)
|
||||
|
||||
def prepare(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def check(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
def run(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
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()
|
||||
try:
|
||||
if self.module.check_mode:
|
||||
self.check()
|
||||
else:
|
||||
self.run()
|
||||
except Exception as exc:
|
||||
import traceback
|
||||
|
||||
self.module.fail_json(
|
||||
"".join(traceback.format_exception(type(exc), exc, exc.__traceback__)),
|
||||
**self.result,
|
||||
)
|
||||
self.module.exit_json(**self.result)
|
||||
|
||||
@classmethod
|
||||
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:
|
||||
import yaml
|
||||
except ImportError:
|
||||
return "---\n"
|
||||
doc = cls.__doc__
|
||||
if doc is None:
|
||||
doc = ""
|
||||
help: _sdict
|
||||
specs = dict()
|
||||
if "argument_spec" in cls._common_args:
|
||||
specs.update(cls._common_args["argument_spec"])
|
||||
if "argument_spec" in cls.module_spec:
|
||||
specs.update(cls.module_spec["argument_spec"])
|
||||
options = docify(specs)
|
||||
docu = doc.split("\n")
|
||||
return str(
|
||||
yaml.safe_dump(
|
||||
dict(
|
||||
module=cls.name,
|
||||
short_description=docu[0],
|
||||
description=docu,
|
||||
options=options,
|
||||
),
|
||||
stream=None,
|
||||
explicit_start="---",
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class SystemdUnitModule(AnsibleModule):
|
||||
#: path of the unitfile managed by this module
|
||||
unitfile: pathlib.Path
|
||||
#: subclasses of this always support the file common args and the check mode
|
||||
_common_args = dict(
|
||||
supports_check_mode=True,
|
||||
add_file_common_args=True,
|
||||
argument_spec=dict(
|
||||
description=Types.str(help="An description for programs that access systemd"),
|
||||
documentation=Types.list(str, help="Paths where documentation can be found"),
|
||||
requires=Types.list(
|
||||
str,
|
||||
help="list of units that this unit requires. If it fails or can't be started this unit fails. without before/after this is started at the same time",
|
||||
),
|
||||
wants=Types.list(str, help="list of units that this unit wants. If it fails or can't be started it does not affect this unit"),
|
||||
partof=Types.list(
|
||||
str,
|
||||
help="list of units that this unit is part of.\nIf the restart this unit does it too, but if this restarts it does not affect the other units.",
|
||||
),
|
||||
before=Types.list(str, help="list of units that this unit needs to be started before this unit."),
|
||||
after=Types.list(str, help="list of units that this unit wants to be started after this unit"),
|
||||
),
|
||||
)
|
||||
#: if defined it will be called after run has changed the unitfile
|
||||
post: Optional[Callable[[], None]]
|
||||
|
||||
def unit(self) -> str:
|
||||
raise NotImplementedError()
|
||||
|
||||
def header(self) -> str:
|
||||
header = "[Unit]\n"
|
||||
header += "\n".join(
|
||||
self.map_param(
|
||||
description="Description",
|
||||
documentation="Documentation",
|
||||
requires="Requires",
|
||||
wants="Wants",
|
||||
partof="PartOf",
|
||||
before="Before",
|
||||
after="After",
|
||||
)
|
||||
)
|
||||
return header
|
||||
|
||||
def map_param(self, **parammap: str):
|
||||
"""maps an dict with keys for an section with given params. The key of the dictionary is the parameter and the value is the key in the unitfile. If an parameter has multiple values it adds multiple entries"""
|
||||
output: list[str] = []
|
||||
for param, key in parammap.items():
|
||||
if self.params[param] is not None:
|
||||
params = self.params[param]
|
||||
if isinstance(params, list):
|
||||
output.extend(("{}={}".format(key, p) for p in params))
|
||||
else:
|
||||
output.append("{}={}".format(key, self.params[param]))
|
||||
return output
|
||||
|
||||
def unitfile_gen(self):
|
||||
path = self.tmpdir / "newunit"
|
||||
with open(path, "w") as unit:
|
||||
unit.write(self.unit())
|
||||
self.module.set_owner_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)
|
||||
if self.unitfile.exists():
|
||||
diff = dict()
|
||||
self.changed = self.changed | self.module.set_owner_if_different(
|
||||
self.unitfile.as_posix(),
|
||||
"root",
|
||||
self.result["changed"],
|
||||
diff,
|
||||
)
|
||||
self.diff(diff)
|
||||
diff = dict()
|
||||
self.changed = self.changed | self.module.set_group_if_different(
|
||||
self.unitfile.as_posix(),
|
||||
"root",
|
||||
self.result["changed"],
|
||||
diff,
|
||||
)
|
||||
self.diff(diff)
|
||||
diff = dict()
|
||||
self.changed = self.changed | self.module.set_mode_if_different(
|
||||
self.unitfile.as_posix(),
|
||||
"0644",
|
||||
self.result["changed"],
|
||||
diff,
|
||||
)
|
||||
self.diff(diff)
|
||||
|
||||
def check(self):
|
||||
self.set("unitfile", self.unitfile.as_posix())
|
||||
self.unitfile_gen()
|
||||
if not self.unitfile.exists():
|
||||
self.diff(before="", after=self.unit(), before_header=self.unitfile.as_posix())
|
||||
self.changed = True
|
||||
else:
|
||||
if self.module.sha256(self.unitfile.as_posix()) != self.module.sha256((self.tmpdir / "newunit").as_posix()):
|
||||
self.changed = True
|
||||
self.diff(
|
||||
before=self.unitfile.read_text(),
|
||||
after=self.unit(),
|
||||
before_header=self.unitfile.as_posix(),
|
||||
)
|
||||
|
||||
def run(self):
|
||||
self.check()
|
||||
if not self.changed:
|
||||
return
|
||||
self.module.atomic_move(
|
||||
src=(self.tmpdir / "newunit").as_posix(),
|
||||
dest=self.unitfile.as_posix(),
|
||||
)
|
||||
if hasattr(self, "post") and self.post is not None:
|
||||
self.post()
|
||||
|
||||
|
||||
_INSTALL_MAPPING = dict(
|
||||
required_by="RequiredBy",
|
||||
wanted_by="WantedBy",
|
||||
)
|
||||
|
||||
|
||||
def installable(_class: Type[SystemdUnitModule]):
|
||||
"""adds the required arguments to the spec and adds the install method for the unit method"""
|
||||
specs = _class.module_spec
|
||||
|
||||
arguments = dict(
|
||||
required_by=Types.list(elements=str, help="systemd units that require this mount"),
|
||||
wanted_by=Types.list(
|
||||
elements=str,
|
||||
help="systemd units that want the mount, but not explicitly require it. Commonly used for target if not service explicitly require it.",
|
||||
),
|
||||
)
|
||||
specs["argument_spec"].update(arguments)
|
||||
|
||||
def install(self: SystemdUnitModule) -> str:
|
||||
output = "[Install]\n"
|
||||
for argument, key in _INSTALL_MAPPING.items():
|
||||
if self.get(argument, False):
|
||||
for unit in self.get(argument):
|
||||
output += "{}={}\n".format(key, unit)
|
||||
return output
|
||||
|
||||
_class.install = install
|
||||
_class.module_spec = specs
|
||||
return _class
|
||||
|
||||
|
||||
class SystemdReloadMixin:
|
||||
def post(self):
|
||||
if not self.changed:
|
||||
return
|
||||
systemctl = self.module.get_bin_path("systemctl", required=True)
|
||||
self.module.run_command([systemctl, "daemon-reload"], check_rc=True)
|
||||
if self.unitfile.stem.endswith("@"):
|
||||
return
|
||||
(rc, _, _) = self.module.run_command([systemctl, "is-enabled", self.unitfile.name], check_rc=False)
|
||||
if rc == 0:
|
||||
self.module.run_command([systemctl, "restart", self.unitfile.name], check_rc=True)
|
|
@ -1,13 +1,13 @@
|
|||
#!/usr/bin/python3
|
||||
import pathlib
|
||||
from typing import List
|
||||
from typing import List, Optional
|
||||
|
||||
try:
|
||||
from ansible_collections.sebastian.systemd.plugins.module_utils.generic import SYSTEMD_NETWORK_CONFIG, Types, modspec
|
||||
from ansible_collections.sebastian.systemd.plugins.module_utils.module import SystemdUnitModule
|
||||
from ansible_module.module_utils.generic import SYSTEMD_NETWORK_CONFIG, Types, modspec # type: ignore[reportMissingImports]
|
||||
from ansible_module.module_utils.module import SystemdUnitModule # type: ignore[reportMissingImports]
|
||||
except ImportError:
|
||||
from plugins.module_utils.generic import SYSTEMD_NETWORK_CONFIG, Types, modspec
|
||||
from plugins.module_utils.module import SystemdUnitModule
|
||||
from ansible_collections.sebastian.base.plugins.module_utils.generic import SYSTEMD_NETWORK_CONFIG, Types, modspec
|
||||
from ansible_collections.sebastian.base.plugins.module_utils.module import SystemdUnitModule
|
||||
|
||||
|
||||
class Module(SystemdUnitModule): # type: ignore
|
||||
|
@ -16,12 +16,26 @@ class Module(SystemdUnitModule): # type: ignore
|
|||
name = "link"
|
||||
module_spec = modspec(
|
||||
argument_spec=dict(
|
||||
mac=Types.str(help="The Mac address of the device"),
|
||||
permanentmac=Types.str(help="The Permanent Mac address advertised by the device"),
|
||||
path=Types.str(help="A shell-style glob matching the persistent path, as exposed by the udev property ID_PATH."),
|
||||
driver=Types.str(help="A glob matching the driver currently bound to the device"),
|
||||
type=Types.str(help="A glob matching the device type, as exposed by networkctl list"),
|
||||
kind=Types.str(help="a glob matching the device kind, as exposed by networkctl status INTERFACE or ip -d link show INTERFACE."),
|
||||
mac=Types.str(help="The Mac address of the device." "An ! before the value matches anything but this value."),
|
||||
permanentmac=Types.str(
|
||||
help="The Permanent Mac address advertised by the device. " "An ! before the value matches anything but this value."
|
||||
),
|
||||
path=Types.str(
|
||||
help="A shell-style glob matching the persistent path, as exposed by the udev property ID_PATH. "
|
||||
"An ! before the value matches anything but this value."
|
||||
),
|
||||
driver=Types.str(
|
||||
help="A glob matching the driver currently bound to the device. " "An ! before the value matches anything but this value."
|
||||
),
|
||||
type=Types.str(
|
||||
help="A glob matching the device type, as exposed by networkctl list. "
|
||||
"An ! before the value matches anything but this value."
|
||||
),
|
||||
kind=Types.str(
|
||||
help="a glob matching the device kind, as exposed by networkctl status INTERFACE or ip -d link show INTERFACE. "
|
||||
"An ! before the value matches anything but this value."
|
||||
),
|
||||
virtualization=Types.str(help="The virtualization type. An ! before the value matches anything but this value."),
|
||||
description=Types.str(help="The description for the link"),
|
||||
name=Types.str(required=True, help="The new name of the device"),
|
||||
mtu=Types.int(help="The maximum Transmission unit for the link"),
|
||||
|
@ -40,10 +54,10 @@ class Module(SystemdUnitModule): # type: ignore
|
|||
|
||||
def unit(self) -> str:
|
||||
if self.__unit is None:
|
||||
self.__unit = "\n".join((self.match(), self.link()))
|
||||
self.__unit = self._unit(self.match(), self.link())
|
||||
return self.__unit
|
||||
|
||||
def match(self) -> str:
|
||||
def match(self) -> Optional[str]:
|
||||
options = self.map_param(
|
||||
mac="MACAddress",
|
||||
permanentmac="PermanentAddress",
|
||||
|
@ -51,10 +65,13 @@ class Module(SystemdUnitModule): # type: ignore
|
|||
driver="Driver",
|
||||
type="Type",
|
||||
kind="Kind",
|
||||
virtualization="Virtualization",
|
||||
)
|
||||
return "[Match]\n" + "\n".join(options)
|
||||
if len(options) == 0:
|
||||
return None
|
||||
return "[Match]\n" + "".join(options)
|
||||
|
||||
def link(self) -> str:
|
||||
def link(self) -> Optional[str]:
|
||||
options = []
|
||||
if self.get("description", False):
|
||||
options.append("Description={}\n".format(self.get("description", False)))
|
||||
|
@ -62,6 +79,8 @@ class Module(SystemdUnitModule): # type: ignore
|
|||
options.append("Name={}\n".format(self.get("name", False)))
|
||||
if self.get("mtu", False):
|
||||
options.append("MTUBytes={}\n".format(self.get("mtu", False)))
|
||||
if len(options) == 0:
|
||||
return None
|
||||
return "[Link]\n" + "".join(options)
|
||||
|
||||
def post(self):
|
||||
|
@ -82,7 +101,7 @@ class Module(SystemdUnitModule): # type: ignore
|
|||
self.module.run_command(args, check_rc=True)
|
||||
|
||||
|
||||
DOCUMENTATION = """---
|
||||
DOCUMENTATION = r"""---
|
||||
description:
|
||||
- generates an systemd-networkd link
|
||||
module: link
|
||||
|
@ -115,18 +134,20 @@ options:
|
|||
type: list
|
||||
driver:
|
||||
description:
|
||||
- A glob matching the driver currently bound to the device
|
||||
- A glob matching the driver currently bound to the device. An ! before the value
|
||||
matches anything but this value.
|
||||
required: false
|
||||
type: str
|
||||
kind:
|
||||
description:
|
||||
- a glob matching the device kind, as exposed by networkctl status INTERFACE or
|
||||
ip -d link show INTERFACE.
|
||||
ip -d link show INTERFACE. An ! before the value matches anything but this value.
|
||||
required: false
|
||||
type: str
|
||||
mac:
|
||||
description:
|
||||
- The Mac address of the device
|
||||
- The Mac address of the device.An ! before the value matches anything but this
|
||||
value.
|
||||
required: false
|
||||
type: str
|
||||
mtu:
|
||||
|
@ -151,12 +172,13 @@ options:
|
|||
path:
|
||||
description:
|
||||
- A shell-style glob matching the persistent path, as exposed by the udev property
|
||||
ID_PATH.
|
||||
ID_PATH. An ! before the value matches anything but this value.
|
||||
required: false
|
||||
type: str
|
||||
permanentmac:
|
||||
description:
|
||||
- The Permanent Mac address advertised by the device
|
||||
- The Permanent Mac address advertised by the device. An ! before the value matches
|
||||
anything but this value.
|
||||
required: false
|
||||
type: str
|
||||
requires:
|
||||
|
@ -169,7 +191,13 @@ options:
|
|||
type: list
|
||||
type:
|
||||
description:
|
||||
- A glob matching the device type, as exposed by networkctl list
|
||||
- A glob matching the device type, as exposed by networkctl list. An ! before
|
||||
the value matches anything but this value.
|
||||
required: false
|
||||
type: str
|
||||
virtualization:
|
||||
description:
|
||||
- The virtualization type. An ! before the value matches anything but this value.
|
||||
required: false
|
||||
type: str
|
||||
wants:
|
||||
|
|
|
@ -3,11 +3,11 @@ import pathlib
|
|||
from typing import List, Optional
|
||||
|
||||
try:
|
||||
from ansible_collections.sebastian.systemd.plugins.module_utils.generic import SYSTEMD_SERVICE_CONFIG, Types, modspec
|
||||
from ansible_collections.sebastian.systemd.plugins.module_utils.module import SystemdReloadMixin, SystemdUnitModule, installable
|
||||
from ansible_module.module_utils.generic import SYSTEMD_SERVICE_CONFIG, Types, modspec # type: ignore[reportMissingImports]
|
||||
from ansible_module.module_utils.module import SystemdReloadMixin, SystemdUnitModule, installable # type: ignore[reportMissingImports]
|
||||
except ImportError:
|
||||
from plugins.module_utils.generic import SYSTEMD_SERVICE_CONFIG, Types, modspec
|
||||
from plugins.module_utils.module import SystemdReloadMixin, SystemdUnitModule, installable
|
||||
from ansible_collections.sebastian.base.plugins.module_utils.generic import SYSTEMD_SERVICE_CONFIG, Types, modspec
|
||||
from ansible_collections.sebastian.base.plugins.module_utils.module import SystemdReloadMixin, SystemdUnitModule, installable
|
||||
|
||||
SYSTEMD_SERVICE_CONFIG = pathlib.Path("/etc/systemd/system")
|
||||
|
||||
|
@ -18,7 +18,7 @@ OPTION_MAPPING = dict(
|
|||
|
||||
|
||||
@installable
|
||||
class Module(SystemdUnitModule, SystemdReloadMixin): # type: ignore
|
||||
class Module(SystemdUnitModule, SystemdReloadMixin): # type: ignore[misc]
|
||||
"""Creates an systemd mount"""
|
||||
|
||||
name = "mount"
|
||||
|
@ -41,33 +41,32 @@ class Module(SystemdUnitModule, SystemdReloadMixin): # type: ignore
|
|||
self.unitfile = SYSTEMD_SERVICE_CONFIG.joinpath(self.mountdir.relative_to("/").as_posix().replace("/", "-")).with_suffix(".mount")
|
||||
self.__unit = None
|
||||
if self.get("description", False) is False:
|
||||
self.params["description"] = "Mount for {}".format(self.mountdir.relative_to("/").as_posix())
|
||||
self.params["description"] = "Mount for {}".format(self.mountdir.as_posix())
|
||||
|
||||
def unit(self) -> str:
|
||||
if self.__unit is None:
|
||||
self.__unit = "\n".join(
|
||||
(
|
||||
self.header(),
|
||||
self.mount(),
|
||||
self.install(),
|
||||
)
|
||||
self.__unit = self._unit(
|
||||
self.header(),
|
||||
self.mount(),
|
||||
self.install(), # type: ignore[misc,call-arg]
|
||||
)
|
||||
return self.__unit
|
||||
|
||||
def header(self) -> str:
|
||||
return "[Unit]\nDescription={}\n".format(self.get("description", "Mount for {}".format(self.get("where"))))
|
||||
description = self.get("description", "Mount for {}".format(self.get("where")))
|
||||
return f"[Unit]\nDescription={description}\n"
|
||||
|
||||
def mount(self) -> str:
|
||||
output = "[Mount]\n"
|
||||
output += "Where={}\n".format(self.get("where"))
|
||||
output += "What={}\n".format(self.get("what"))
|
||||
output += "Type={}\n".format(self.get("fs"))
|
||||
options = []
|
||||
options.append("Where={}\n".format(self.get("where")))
|
||||
options.append("What={}\n".format(self.get("what")))
|
||||
options.append("Type={}\n".format(self.get("fs")))
|
||||
if self.get("options", False):
|
||||
output += "Options={}\n".format(",".join(self.get("options")))
|
||||
return output
|
||||
options.append("Options={}\n".format(",".join(self.get("options"))))
|
||||
return "[Mount]\n" + "".join(options)
|
||||
|
||||
|
||||
DOCUMENTATION = """---
|
||||
DOCUMENTATION = r"""---
|
||||
description:
|
||||
- Creates an systemd mount
|
||||
module: mount
|
||||
|
|
211
plugins/modules/netdev.py
Normale Datei
211
plugins/modules/netdev.py
Normale Datei
|
@ -0,0 +1,211 @@
|
|||
#!/usr/bin/python3
|
||||
import pathlib
|
||||
from typing import List, Optional
|
||||
|
||||
try:
|
||||
from ansible_module.module_utils.generic import SYSTEMD_NETWORK_CONFIG, Types, joindict, modspec # type: ignore[reportMissingImports]
|
||||
from ansible_module.module_utils.module import SystemdUnitModule # type: ignore[reportMissingImports]
|
||||
except ImportError:
|
||||
from ansible_collections.sebastian.base.plugins.module_utils.generic import (
|
||||
SYSTEMD_NETWORK_CONFIG,
|
||||
Types,
|
||||
joindict,
|
||||
modspec,
|
||||
)
|
||||
from ansible_collections.sebastian.base.plugins.module_utils.module import SystemdUnitModule
|
||||
|
||||
kinds = (
|
||||
"bond",
|
||||
"bridge",
|
||||
"dummy",
|
||||
"wireguard",
|
||||
"vlan",
|
||||
)
|
||||
|
||||
matchspec = dict(
|
||||
host=Types.str(help="hostname of the host that is matched against"),
|
||||
virtualization=Types.str(help="Virtualization that is checked against"),
|
||||
kernelcmd=Types.str(help="checks an kernel commandline argument"),
|
||||
)
|
||||
|
||||
netdevspec = dict(
|
||||
description=Types.str(help="Description of the device"),
|
||||
name=Types.str(required=True, help="name of the device"),
|
||||
kind=Types.str(required=True, help="type of the device", choices=kinds),
|
||||
)
|
||||
|
||||
bridgespec = dict(
|
||||
stp=Types.bool(help="enable the stp protocol"),
|
||||
priority=Types.int(help="Priority of the bridge"),
|
||||
)
|
||||
|
||||
bondspec = dict(
|
||||
mode=Types.str(
|
||||
help="bonding policy",
|
||||
choices=("balance-rr", "active-backup", "balance-xor", "broadcast", "802.3ad", "balance-tlb", "balance-alb"),
|
||||
),
|
||||
minlinks=Types.int(help="Specifies the minimum number of links that must be active before asserting carrier."),
|
||||
)
|
||||
|
||||
wireguardspec = dict(
|
||||
privatekey=Types.str(help="private key of this side of the tunnel"),
|
||||
privatekeyfile=Types.path(help="Path of the private key on the host."),
|
||||
port=Types.raw(help="Port that wireguard uses to listen for packets. the value 'auto' means that the port is automatically decided."),
|
||||
)
|
||||
|
||||
|
||||
class Module(SystemdUnitModule): # type:ignore[reportGeneralTypeIssues]
|
||||
"""Creates an netdev unit that creates an virtual devices"""
|
||||
|
||||
name = "netdev"
|
||||
module_spec = modspec(argument_spec=joindict(bondspec, bridgespec, matchspec, netdevspec))
|
||||
_common_args = dict(
|
||||
supports_check_mode=True,
|
||||
add_file_common_args=True,
|
||||
)
|
||||
|
||||
def prepare(self):
|
||||
self.__unit = None
|
||||
self.unitfile = SYSTEMD_NETWORK_CONFIG.joinpath(self.get("name")).with_suffix(".netdev")
|
||||
|
||||
def unit(self):
|
||||
if self.__unit is None:
|
||||
kind = self.get("kind")
|
||||
parts = [self.match(), self.netdev()]
|
||||
if kind != "dummy":
|
||||
parts.append(getattr(self, kind)())
|
||||
self.__unit = self._unit(*parts)
|
||||
return self.__unit
|
||||
|
||||
def match(self) -> Optional[str]:
|
||||
options = self.map_param(
|
||||
host="Host",
|
||||
kernelcmd="KernelCommandLine",
|
||||
virtualization="Virtualization",
|
||||
)
|
||||
if len(options) == 0:
|
||||
return None
|
||||
return "[Match]\n" + "".join(options)
|
||||
|
||||
def netdev(self) -> Optional[str]:
|
||||
options = self.map_param(
|
||||
description="Description",
|
||||
name="Name",
|
||||
kind="Kind",
|
||||
)
|
||||
if len(options) == 0:
|
||||
return None
|
||||
return "[NetDev]\n" + "".join(options)
|
||||
|
||||
def bond(self) -> Optional[str]:
|
||||
options = self.map_param(
|
||||
mode="Mode",
|
||||
minlinks="MinLinks",
|
||||
)
|
||||
if len(options) == 0:
|
||||
return None
|
||||
return "[Bond]\n" + "".join(options)
|
||||
|
||||
def bridge(self) -> Optional[str]:
|
||||
options = self.map_param(
|
||||
stp="STP",
|
||||
priority="Priority",
|
||||
)
|
||||
if len(options) == 0:
|
||||
return None
|
||||
return "[Bridge]\n" + "".join(options)
|
||||
|
||||
def wireguard(self) -> Optional[str]:
|
||||
options = self.map_param(
|
||||
privatekey="PrivateKey",
|
||||
privatekeyfile="PrivateKeyFile",
|
||||
port="ListenPort",
|
||||
)
|
||||
port = self.get("port", False)
|
||||
if port not in (False, "auto") and (port < 1 or port > 65535):
|
||||
raise ValueError("Port must be between 0 and 65536")
|
||||
if len(options) == 0:
|
||||
return None
|
||||
return "[Wireguard]\n" + "".join(options)
|
||||
|
||||
def vlan(self) -> Optional[str]:
|
||||
options: list[str] = []
|
||||
if len(options) == 0:
|
||||
return None
|
||||
return "[Vlan]\n" + "".join(options)
|
||||
|
||||
|
||||
DOCUMENTATION = r"""---
|
||||
description:
|
||||
- Creates an netdev unit that creates an virtual devices
|
||||
module: netdev
|
||||
options:
|
||||
description:
|
||||
description:
|
||||
- Description of the device
|
||||
required: false
|
||||
type: str
|
||||
host:
|
||||
description:
|
||||
- hostname of the host that is matched against
|
||||
required: false
|
||||
type: str
|
||||
kernelcmd:
|
||||
description:
|
||||
- checks an kernel commandline argument
|
||||
required: false
|
||||
type: str
|
||||
kind:
|
||||
choices:
|
||||
- bond
|
||||
- bridge
|
||||
- dummy
|
||||
- wireguard
|
||||
- vlan
|
||||
description:
|
||||
- type of the device
|
||||
required: true
|
||||
type: str
|
||||
minlinks:
|
||||
description:
|
||||
- Specifies the minimum number of links that must be active before asserting carrier.
|
||||
required: false
|
||||
type: int
|
||||
mode:
|
||||
choices:
|
||||
- balance-rr
|
||||
- active-backup
|
||||
- balance-xor
|
||||
- broadcast
|
||||
- 802.3ad
|
||||
- balance-tlb
|
||||
- balance-alb
|
||||
description:
|
||||
- bonding policy
|
||||
required: false
|
||||
type: str
|
||||
name:
|
||||
description:
|
||||
- name of the device
|
||||
required: true
|
||||
type: str
|
||||
priority:
|
||||
description:
|
||||
- Priority of the bridge
|
||||
required: false
|
||||
type: int
|
||||
stp:
|
||||
description:
|
||||
- enable the stp protocol
|
||||
required: false
|
||||
type: bool
|
||||
virtualization:
|
||||
description:
|
||||
- Virtualization that is checked against
|
||||
required: false
|
||||
type: str
|
||||
short_description: Creates an netdev unit that creates an virtual devices
|
||||
"""
|
||||
|
||||
if __name__ == "__main__":
|
||||
Module()()
|
|
@ -1,13 +1,13 @@
|
|||
#!/usr/bin/python3
|
||||
import pathlib
|
||||
from typing import List, Union
|
||||
from typing import List, Optional, Union
|
||||
|
||||
try:
|
||||
from ansible_collections.sebastian.systemd.plugins.module_utils.generic import SYSTEMD_NETWORK_CONFIG, Types, modspec, systemdbool
|
||||
from ansible_collections.sebastian.systemd.plugins.module_utils.module import SystemdUnitModule
|
||||
from ansible_module.module_utils.generic import SYSTEMD_NETWORK_CONFIG, Types, modspec, systemdbool # type: ignore[reportMissingImports]
|
||||
from ansible_module.module_utils.module import SystemdUnitModule # type: ignore[reportMissingImports]
|
||||
except ImportError:
|
||||
from plugins.module_utils.generic import SYSTEMD_NETWORK_CONFIG, Types, modspec, systemdbool
|
||||
from plugins.module_utils.module import SystemdUnitModule
|
||||
from ansible_collections.sebastian.base.plugins.module_utils.generic import SYSTEMD_NETWORK_CONFIG, Types, modspec, systemdbool
|
||||
from ansible_collections.sebastian.base.plugins.module_utils.module import SystemdUnitModule
|
||||
|
||||
|
||||
class Module(SystemdUnitModule): # type: ignore
|
||||
|
@ -16,8 +16,9 @@ class Module(SystemdUnitModule): # type: ignore
|
|||
name = "network"
|
||||
module_spec = modspec(
|
||||
argument_spec=dict(
|
||||
mac=Types.str(help="The MAC-Address of the device"),
|
||||
device=Types.str(help="The name of the network device"),
|
||||
mac=Types.str(help="The MAC-Address of the device. An ! before the value matches anything but this value."),
|
||||
device=Types.str(help="The name of the network device. An ! before the value matches anything but this value."),
|
||||
virtualization=Types.str(help="The virtualization type. An ! before the value matches anything but this value."),
|
||||
name=Types.str(required=True, help="name of the unit"),
|
||||
dot=Types.bool(help="if DNS-over-TLS should be required or disabled. If it is unset, it will used if the server supports it"),
|
||||
dnssec=Types.bool(
|
||||
|
@ -33,9 +34,13 @@ class Module(SystemdUnitModule): # type: ignore
|
|||
elements=str,
|
||||
help="Routes of networks that can be reached with this device",
|
||||
),
|
||||
masquerade=Types.str(
|
||||
help="how the packets are modified to look like the come from the computer itself.",
|
||||
choices=("true", "false", "both", "ipv4", "ipv6", "no"),
|
||||
),
|
||||
),
|
||||
required_if=(("defaultdns", True, ("dns",), False),),
|
||||
required_one_of=(("mac", "device"),),
|
||||
required_one_of=(("mac", "device", "virtualization"),),
|
||||
)
|
||||
|
||||
def prepare(self):
|
||||
|
@ -44,47 +49,46 @@ class Module(SystemdUnitModule): # type: ignore
|
|||
|
||||
def unit(self) -> str:
|
||||
if self.__unit is None:
|
||||
self.__unit = "\n".join(
|
||||
(
|
||||
self.match(),
|
||||
self.network(),
|
||||
self.addresses(),
|
||||
self.routes(),
|
||||
)
|
||||
self.__unit = self._unit(
|
||||
self.match(),
|
||||
self.network(),
|
||||
self.addresses(),
|
||||
self.routes(),
|
||||
)
|
||||
return self.__unit
|
||||
|
||||
def match(self) -> str:
|
||||
matches = []
|
||||
if self.get("mac", False):
|
||||
matches.append("MACAddress={}\n".format(self.get("mac")))
|
||||
if self.get("device", False):
|
||||
matches.append("Name={}\n".format(self.get("device")))
|
||||
def match(self) -> Optional[str]:
|
||||
matches = self.map_param(
|
||||
mac="MACAddress",
|
||||
device="Name",
|
||||
virtualization="Virtualization",
|
||||
)
|
||||
if len(matches) == 0:
|
||||
return None
|
||||
return "[Match]\n" + "".join(matches)
|
||||
|
||||
def network(self) -> str:
|
||||
output = "[Network]\n"
|
||||
def network(self) -> Optional[str]:
|
||||
options = []
|
||||
try:
|
||||
if self.get("description", None) is None:
|
||||
options.append("Description={}".format(self.get("description")))
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
for server in self.get("dns", []):
|
||||
options.append(f"DNS={server}")
|
||||
options.append("DNSDefaultRoute={}".format(self.get("defaultdns", False)))
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
domain = self.get("domain")
|
||||
self.set("domainlog", str(domain))
|
||||
options.append("Domains={}".format(" ".join(domain)))
|
||||
server: str
|
||||
for server in self.get("dns", []):
|
||||
options.append(f"DNS={server}")
|
||||
options.append("DNSDefaultRoute={}".format(self.get("defaultdns", False)))
|
||||
if self.get("domain", False):
|
||||
options.append("Domains={}".format(" ".join(self.get("domain"))))
|
||||
options.append("DNSOverTLS={}".format(systemdbool(self.get("dot", "opportunistic"))))
|
||||
options.append("DNSSEC={}".format(systemdbool(self.get("dnssec", "allow-downgrade"))))
|
||||
except KeyError:
|
||||
pass
|
||||
output += "\n".join(options)
|
||||
return output
|
||||
if self.get("masquerade", None) is not None:
|
||||
masquerade: str = self.get("masquerade")
|
||||
if masquerade == "true":
|
||||
masquerade = "both"
|
||||
elif masquerade == "false":
|
||||
masquerade = "no"
|
||||
options.append(f"IPMasquerade={masquerade}")
|
||||
if len(options) == 0:
|
||||
return None
|
||||
return "[Network]\n" + "".join(options)
|
||||
|
||||
def addresses(self) -> str:
|
||||
output = []
|
||||
|
@ -92,17 +96,17 @@ class Module(SystemdUnitModule): # type: ignore
|
|||
output.append(f"[Address]\nAddress={address}\n")
|
||||
return "\n".join(output)
|
||||
|
||||
def routes(self) -> str:
|
||||
def routes(self) -> Optional[str]:
|
||||
output = []
|
||||
routes = self.get("route", [])
|
||||
self.set("routes", routes)
|
||||
routes: list[str] = self.get("route", [])
|
||||
for gw in routes:
|
||||
output.append(f"[Route]\nGateway={gw}\nGatewayOnLink=yes\nQuickAck=yes\n")
|
||||
self.set("routes", output)
|
||||
if len(output) == 0:
|
||||
return None
|
||||
return "\n".join(output)
|
||||
|
||||
|
||||
DOCUMENTATION = """---
|
||||
DOCUMENTATION = r"""---
|
||||
description:
|
||||
- Sets up the systemd network unit
|
||||
module: network
|
||||
|
@ -140,7 +144,8 @@ options:
|
|||
type: str
|
||||
device:
|
||||
description:
|
||||
- The name of the network device
|
||||
- The name of the network device. An ! before the value matches anything but this
|
||||
value.
|
||||
required: false
|
||||
type: str
|
||||
dns:
|
||||
|
@ -179,7 +184,20 @@ options:
|
|||
type: bool
|
||||
mac:
|
||||
description:
|
||||
- The MAC-Address of the device
|
||||
- The MAC-Address of the device. An ! before the value matches anything but this
|
||||
value.
|
||||
required: false
|
||||
type: str
|
||||
masquerade:
|
||||
choices:
|
||||
- 'true'
|
||||
- 'false'
|
||||
- both
|
||||
- ipv4
|
||||
- ipv6
|
||||
- 'no'
|
||||
description:
|
||||
- how the packets are modified to look like the come from the computer itself.
|
||||
required: false
|
||||
type: str
|
||||
name:
|
||||
|
@ -211,6 +229,11 @@ options:
|
|||
elements: str
|
||||
required: false
|
||||
type: list
|
||||
virtualization:
|
||||
description:
|
||||
- The virtualization type. An ! before the value matches anything but this value.
|
||||
required: false
|
||||
type: str
|
||||
wants:
|
||||
default: []
|
||||
description:
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
#!/usr/bin/python3
|
||||
import pathlib
|
||||
from typing import List, Union
|
||||
from typing import List, Optional, Union
|
||||
|
||||
try:
|
||||
from ansible_collections.sebastian.systemd.plugins.module_utils.generic import SYSTEMD_SERVICE_CONFIG, Types, modspec
|
||||
from ansible_collections.sebastian.systemd.plugins.module_utils.module import SystemdReloadMixin, SystemdUnitModule, installable
|
||||
from ansible_module.module_utils.generic import SYSTEMD_SERVICE_CONFIG, Types, modspec # type: ignore[reportMissingImports]
|
||||
from ansible_module.module_utils.module import SystemdReloadMixin, SystemdUnitModule, installable # type: ignore[reportMissingImports]
|
||||
except ImportError:
|
||||
from plugins.module_utils.generic import SYSTEMD_SERVICE_CONFIG, Types, modspec
|
||||
from plugins.module_utils.module import SystemdReloadMixin, SystemdUnitModule, installable
|
||||
from ansible_collections.sebastian.base.plugins.module_utils.generic import SYSTEMD_SERVICE_CONFIG, Types, modspec
|
||||
from ansible_collections.sebastian.base.plugins.module_utils.module import SystemdReloadMixin, SystemdUnitModule, installable
|
||||
|
||||
|
||||
@installable
|
||||
|
@ -19,21 +19,27 @@ class Module(SystemdUnitModule, SystemdReloadMixin): # type: ignore
|
|||
argument_spec=dict(
|
||||
name=Types.str(required=True, help="Name of the socket"),
|
||||
stream=Types.list(
|
||||
Types.str(
|
||||
help="Ip-Addresses or paths for Stream-sockets, e.g. normal unix-sockets or TCP sockets",
|
||||
elements=Types.str(
|
||||
help="Name of the stream socket. The name can be a path, an portnumber or an ip with an port. addresses in square brackets are always ipv6 addresses"
|
||||
)
|
||||
),
|
||||
),
|
||||
datagram=Types.list(
|
||||
Types.str(
|
||||
help="Ip-Addresses or paths for Datagram-sockets, e.g. Datagram unix-sockets or UDP sockets",
|
||||
elements=Types.str(
|
||||
help="Name of the datagram socket. The name can be a path, an portnumber or an ip with an port. addresses in square brackets are always ipv6 addresses"
|
||||
)
|
||||
),
|
||||
),
|
||||
sequential=Types.list(
|
||||
Types.str(
|
||||
help="Ip-Addresses or paths for Sequential Sockets.",
|
||||
elements=Types.str(
|
||||
help="Name of the sequential socket. The name can be a path, an portnumber or an ip with an port. addresses in square brackets are always ipv6 addresses"
|
||||
)
|
||||
),
|
||||
),
|
||||
fifo=Types.list(
|
||||
help="List of paths for FIFOs(First In First Out)",
|
||||
elements=Types.path(help="Name of the fifo. The name must be an absolute path"),
|
||||
),
|
||||
fifo=Types.list(Types.path(help="Name of the fifo. The name must be an absolute path")),
|
||||
socketuser=Types.str(help="User that owns the socket/fifo"),
|
||||
socketgroup=Types.str(help="Group that owns the socket/fifo"),
|
||||
socketmode=Types.str(help="mode of the socket in octal notation", default="0666"),
|
||||
|
@ -41,39 +47,37 @@ class Module(SystemdUnitModule, SystemdReloadMixin): # type: ignore
|
|||
),
|
||||
required_one_of=(("stream", "datagram", "sequential", "fifo"),),
|
||||
)
|
||||
restartable = False
|
||||
|
||||
def prepare(self):
|
||||
self.unitfile = (SYSTEMD_SERVICE_CONFIG / self.get("name")).with_suffix(".socket")
|
||||
self.__unit = None
|
||||
|
||||
def socket(self):
|
||||
section = "[Socket]\n"
|
||||
section += "\n".join(
|
||||
self.map_param(
|
||||
stream="ListenStream",
|
||||
datagram="ListenDatagram",
|
||||
sequential="ListenSequential",
|
||||
fifo="ListenFIFO",
|
||||
socketuser="SocketUser",
|
||||
socketgroup="SocketGroup",
|
||||
socketmode="SocketMode",
|
||||
)
|
||||
def socket(self) -> Optional[str]:
|
||||
section = self.map_param(
|
||||
stream="ListenStream",
|
||||
datagram="ListenDatagram",
|
||||
sequential="ListenSequential",
|
||||
fifo="ListenFIFO",
|
||||
socketuser="SocketUser",
|
||||
socketgroup="SocketGroup",
|
||||
socketmode="SocketMode",
|
||||
)
|
||||
return section
|
||||
if len(section) == 0:
|
||||
return None
|
||||
return "[Socket]\n" + "".join(section)
|
||||
|
||||
def unit(self) -> str:
|
||||
if self.__unit is None:
|
||||
self.__unit = "\n".join(
|
||||
(
|
||||
self.header(),
|
||||
self.socket(),
|
||||
self.install(),
|
||||
)
|
||||
self.__unit = self._unit(
|
||||
self.header(),
|
||||
self.socket(),
|
||||
self.install(), # type: ignore[call-arg,misc]
|
||||
)
|
||||
return self.__unit
|
||||
|
||||
|
||||
DOCUMENTATION = """---
|
||||
DOCUMENTATION = r"""---
|
||||
description:
|
||||
- Creates socket units.
|
||||
module: socket
|
||||
|
@ -94,6 +98,9 @@ options:
|
|||
type: list
|
||||
datagram:
|
||||
default: []
|
||||
description:
|
||||
- Ip-Addresses or paths for Datagram-sockets, e.g. Datagram unix-sockets or UDP
|
||||
sockets
|
||||
elements: str
|
||||
required: false
|
||||
type: list
|
||||
|
@ -111,6 +118,8 @@ options:
|
|||
type: list
|
||||
fifo:
|
||||
default: []
|
||||
description:
|
||||
- List of paths for FIFOs(First In First Out)
|
||||
elements: path
|
||||
required: false
|
||||
type: list
|
||||
|
@ -145,6 +154,8 @@ options:
|
|||
type: list
|
||||
sequential:
|
||||
default: []
|
||||
description:
|
||||
- Ip-Addresses or paths for Sequential Sockets.
|
||||
elements: str
|
||||
required: false
|
||||
type: list
|
||||
|
@ -173,6 +184,8 @@ options:
|
|||
type: str
|
||||
stream:
|
||||
default: []
|
||||
description:
|
||||
- Ip-Addresses or paths for Stream-sockets, e.g. normal unix-sockets or TCP sockets
|
||||
elements: str
|
||||
required: false
|
||||
type: list
|
||||
|
|
|
@ -3,15 +3,15 @@ import pathlib
|
|||
from typing import List, Union
|
||||
|
||||
try:
|
||||
from ansible_collections.sebastian.systemd.plugins.module_utils.generic import SYSTEMD_SERVICE_CONFIG, Types
|
||||
from ansible_collections.sebastian.systemd.plugins.module_utils.module import SystemdReloadMixin, SystemdUnitModule, installable
|
||||
from ansible_module.module_utils.generic import SYSTEMD_SERVICE_CONFIG, Types # type: ignore[reportMissingImports]
|
||||
from ansible_module.module_utils.module import SystemdReloadMixin, SystemdUnitModule, installable # type: ignore[reportMissingImports]
|
||||
except ImportError:
|
||||
from plugins.module_utils.generic import SYSTEMD_SERVICE_CONFIG, Types
|
||||
from plugins.module_utils.module import SystemdReloadMixin, SystemdUnitModule, installable
|
||||
from ansible_collections.sebastian.base.plugins.module_utils.generic import SYSTEMD_SERVICE_CONFIG, Types
|
||||
from ansible_collections.sebastian.base.plugins.module_utils.module import SystemdReloadMixin, SystemdUnitModule, installable
|
||||
|
||||
|
||||
@installable
|
||||
class Module(SystemdUnitModule, SystemdReloadMixin):
|
||||
class Module(SystemdUnitModule, SystemdReloadMixin): # type: ignore[misc]
|
||||
"""Creates System Services units"""
|
||||
|
||||
name = "system_service"
|
||||
|
@ -31,24 +31,84 @@ class Module(SystemdUnitModule, SystemdReloadMixin):
|
|||
"notify and notify-reload notify systemd about the start up via sd_notify. notify-reload needs also inform systemd on reloads and when it is ready again after an reload.\n"
|
||||
"idle is similar to simple, but it can delay the start up by a few seconds.",
|
||||
),
|
||||
pre=Types.list(str, help="command or list of commands that are started before the main command(Types.str)"),
|
||||
pre=Types.list(elements=str, help="command or list of commands that are started before the main command"),
|
||||
start=Types.list(
|
||||
str,
|
||||
True,
|
||||
elements=str,
|
||||
required=True,
|
||||
help="command or list of commands that are started as main programm. Multiple commands are only allowed in a oneshot command",
|
||||
),
|
||||
stop=Types.str(help="command that is started to stop the main program."),
|
||||
remain=Types.bool(help="should the service remain as started after the command exited"),
|
||||
post=Types.list(str, help="Command or list of commands that are started after the main command(s) stopped without problems."),
|
||||
environmentfile=Types.list(
|
||||
str, help="List of file that are containing environment variables. They are evaluated before each pre/start/post command"
|
||||
elements=str,
|
||||
help="List of file that are containing environment variables. They are evaluated before each pre/start/post command",
|
||||
),
|
||||
environment=Types.list(
|
||||
Types.dict(
|
||||
elements=Types.dict(
|
||||
help="An Environment Variable",
|
||||
name=Types.str(help="name of the Environment variable", required=True),
|
||||
value=Types.str(help="value of the Environment variable", required=True),
|
||||
),
|
||||
help="List of environment variables that are set to each command before they run",
|
||||
),
|
||||
workingdirectory=Types.str(help="The Directory that is used for the processes as current working directory"),
|
||||
workingdirectory=Types.str(
|
||||
help="The Directory that is used for the processes as current working directory",
|
||||
),
|
||||
rwpath=Types.list(
|
||||
elements=Types.path(help="An Read and writable Path"),
|
||||
help="Path(s) that are readable and writable (if permission allow)",
|
||||
),
|
||||
ropath=Types.list(
|
||||
elements=Types.path(help="An Read and writable Path"),
|
||||
help="Path(s) that are read only",
|
||||
),
|
||||
notreadablepath=Types.list(
|
||||
elements=Types.path(help="An Read and writable Path"),
|
||||
help="Path(s) that are not accessible by the applications",
|
||||
),
|
||||
execpath=Types.list(
|
||||
elements=Types.path(help="An Read and writable Path"),
|
||||
help="Path(s) where executable files are",
|
||||
),
|
||||
noexecpath=Types.list(
|
||||
elements=Types.path(help="An Read and writable Path"),
|
||||
help="Path(s) which are never executable (uploaded files, user accessible paths)",
|
||||
),
|
||||
protecthome=Types.str(
|
||||
help="if true makes user specific directories (/home, /root, /run/user) inaccessible. read-only makes them read only and tmpfs is useful to create binds in it",
|
||||
choices=("true", "false", "read-only", "tmpfs"),
|
||||
),
|
||||
protectsystem=Types.str(
|
||||
help="makes the system read only. if true /usr, /boot and /efi are read only, if full additionally /etc and if strict all except /proc, /sys and /dev",
|
||||
choices=("true", "false", "full", "strict"),
|
||||
),
|
||||
nonewprivileges=Types.bool(
|
||||
help="disables the ability to get new capabilities for processes than already granted ones",
|
||||
),
|
||||
statedirectory=Types.str(
|
||||
help="creates an unit specific state directory in /var/lib and sets the env var STATE_DIRECTORY with the path to it. Its cleaned up after the unit is stopped"
|
||||
),
|
||||
runtimedirectory=Types.str(
|
||||
help="creates an unit specific runtime directory in /run and sets the env var RUNTIME_DIRECTORY with the path to it. Its cleaned up after the unit is stopped"
|
||||
),
|
||||
restart=Types.str(
|
||||
help=[
|
||||
"Controls when the service is automatically restarted",
|
||||
"no means never",
|
||||
"on-success when it exits with an exitcode of 0 or one of the SuccessExitStatus(not implemented) values",
|
||||
"on-failure is the reverse of the on-success value",
|
||||
"on-abnormal is when an service has to be killed or doesn't react to notifications from systemd, if its of the notify type",
|
||||
"on-abort means if it has to be killed, because it didn't react to signals",
|
||||
"on-watchdog when services missed the regular ping or other important messages",
|
||||
"always means what it is: restart it whenever it is stopped",
|
||||
"This setting is invalid for oneshot services",
|
||||
],
|
||||
choices=("no", "on-success", "on-failure", "on-abnormal", "on-watchdog", "on-abort", "always"),
|
||||
),
|
||||
restartsec=Types.str(
|
||||
help="this is the time it is waited between the stop of an service and its restart. this can be an time in seconds or an timespan like '5m 20s'"
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
|
@ -56,38 +116,56 @@ class Module(SystemdUnitModule, SystemdReloadMixin):
|
|||
self.unitfile = (SYSTEMD_SERVICE_CONFIG / self.get("name")).with_suffix(".service")
|
||||
self.__unit = None
|
||||
if self.get("type", "simple") != "oneshot" and len(self.get("start")) > 1:
|
||||
self.module.fail_json("only oneshot services are allowed to have multiple start commands", **self.result)
|
||||
self.fail("only oneshot services are allowed to have multiple start commands")
|
||||
|
||||
def service(self):
|
||||
section = "[Service]\n"
|
||||
section += "\n".join(
|
||||
def service(self) -> str:
|
||||
params = []
|
||||
if self.get("environment", False):
|
||||
for env in self.get("environment"):
|
||||
params.append(f"Environment={env['name']}={env['value']}\n")
|
||||
params.extend(
|
||||
self.map_param(
|
||||
type="Type",
|
||||
pre="ExecStartPre",
|
||||
start="ExecStart",
|
||||
stop="ExecStop",
|
||||
post="ExecStartPost",
|
||||
serviceuser="User",
|
||||
servicegroup="Group",
|
||||
workingdirectory="WorkingDirectory",
|
||||
environmentfile="EnvironmentFile",
|
||||
protecthome="ProtectHome",
|
||||
protectsystem="ProtectSystem",
|
||||
rwpath="ReadWritePaths",
|
||||
ropath="ReadOnlyPaths",
|
||||
notreadablepath="InaccessiblePaths",
|
||||
execpath="ExecPaths",
|
||||
noexecpath="NoExecPaths",
|
||||
statedirectory="StateDirectory",
|
||||
runtimedirectory="RuntimeDirectory",
|
||||
nonewprivileges="NoNewPriviledges",
|
||||
remain="RemainAfterExit",
|
||||
restart="Restart",
|
||||
restartsec="RestartSec",
|
||||
)
|
||||
)
|
||||
return section
|
||||
return "[Service]\n" + "".join(params)
|
||||
|
||||
def unit(self) -> str:
|
||||
if self.__unit is None:
|
||||
self.__unit = "\n".join(
|
||||
(
|
||||
self.header(),
|
||||
self.service(),
|
||||
self.install(),
|
||||
)
|
||||
self.__unit = self._unit(
|
||||
self.header(),
|
||||
self.service(),
|
||||
self.install(), # type: ignore[call-arg,misc]
|
||||
)
|
||||
return self.__unit
|
||||
|
||||
|
||||
DOCUMENTATION = """---
|
||||
DOCUMENTATION = r"""---
|
||||
description:
|
||||
- Creates System Services units
|
||||
extends_documentation_fragment:
|
||||
- ansible.builtin.files
|
||||
module: system_service
|
||||
options:
|
||||
after:
|
||||
|
@ -142,11 +220,38 @@ options:
|
|||
elements: str
|
||||
required: false
|
||||
type: list
|
||||
execpath:
|
||||
default: []
|
||||
description:
|
||||
- Path(s) where executable files are
|
||||
elements: path
|
||||
required: false
|
||||
type: list
|
||||
name:
|
||||
description:
|
||||
- Name of the service
|
||||
required: true
|
||||
type: str
|
||||
noexecpath:
|
||||
default: []
|
||||
description:
|
||||
- Path(s) which are never executable (uploaded files, user accessible paths)
|
||||
elements: path
|
||||
required: false
|
||||
type: list
|
||||
nonewprivileges:
|
||||
description:
|
||||
- disables the ability to get new capabilities for processes than already granted
|
||||
ones
|
||||
required: false
|
||||
type: bool
|
||||
notreadablepath:
|
||||
default: []
|
||||
description:
|
||||
- Path(s) that are not accessible by the applications
|
||||
elements: path
|
||||
required: false
|
||||
type: list
|
||||
partof:
|
||||
default: []
|
||||
description:
|
||||
|
@ -167,10 +272,37 @@ options:
|
|||
pre:
|
||||
default: []
|
||||
description:
|
||||
- command or list of commands that are started before the main command(Types.str)
|
||||
- command or list of commands that are started before the main command
|
||||
elements: str
|
||||
required: false
|
||||
type: list
|
||||
protecthome:
|
||||
choices:
|
||||
- 'true'
|
||||
- 'false'
|
||||
- read-only
|
||||
- tmpfs
|
||||
description:
|
||||
- if true makes user specific directories (/home, /root, /run/user) inaccessible.
|
||||
read-only makes them read only and tmpfs is useful to create binds in it
|
||||
required: false
|
||||
type: str
|
||||
protectsystem:
|
||||
choices:
|
||||
- 'true'
|
||||
- 'false'
|
||||
- full
|
||||
- strict
|
||||
description:
|
||||
- makes the system read only. if true /usr, /boot and /efi are read only, if full
|
||||
additionally /etc and if strict all except /proc, /sys and /dev
|
||||
required: false
|
||||
type: str
|
||||
remain:
|
||||
description:
|
||||
- should the service remain as started after the command exited
|
||||
required: false
|
||||
type: bool
|
||||
required_by:
|
||||
default: []
|
||||
description:
|
||||
|
@ -186,6 +318,55 @@ options:
|
|||
elements: str
|
||||
required: false
|
||||
type: list
|
||||
restart:
|
||||
choices:
|
||||
- 'no'
|
||||
- on-success
|
||||
- on-failure
|
||||
- on-abnormal
|
||||
- on-watchdog
|
||||
- on-abort
|
||||
- always
|
||||
description:
|
||||
- Controls when the service is automatically restarted
|
||||
- no means never
|
||||
- on-success when it exits with an exitcode of 0 or one of the SuccessExitStatus(not
|
||||
implemented) values
|
||||
- on-failure is the reverse of the on-success value
|
||||
- on-abnormal is when an service has to be killed or doesn't react to notifications
|
||||
from systemd, if its of the notify type
|
||||
- on-abort means if it has to be killed, because it didn't react to signals
|
||||
- on-watchdog when services missed the regular ping or other important messages
|
||||
- 'always means what it is: restart it whenever it is stopped'
|
||||
- This setting is invalid for oneshot services
|
||||
required: false
|
||||
type: str
|
||||
restartsec:
|
||||
description:
|
||||
- this is the time it is waited between the stop of an service and its restart.
|
||||
this can be an time in seconds or an timespan like '5m 20s'
|
||||
required: false
|
||||
type: str
|
||||
ropath:
|
||||
default: []
|
||||
description:
|
||||
- Path(s) that are read only
|
||||
elements: path
|
||||
required: false
|
||||
type: list
|
||||
runtimedirectory:
|
||||
description:
|
||||
- creates an unit specific runtime directory in /run and sets the env var RUNTIME_DIRECTORY
|
||||
with the path to it. Its cleaned up after the unit is stopped
|
||||
required: false
|
||||
type: str
|
||||
rwpath:
|
||||
default: []
|
||||
description:
|
||||
- Path(s) that are readable and writable (if permission allow)
|
||||
elements: path
|
||||
required: false
|
||||
type: list
|
||||
servicegroup:
|
||||
default: root
|
||||
description:
|
||||
|
@ -205,6 +386,17 @@ options:
|
|||
elements: str
|
||||
required: true
|
||||
type: list
|
||||
statedirectory:
|
||||
description:
|
||||
- creates an unit specific state directory in /var/lib and sets the env var STATE_DIRECTORY
|
||||
with the path to it. Its cleaned up after the unit is stopped
|
||||
required: false
|
||||
type: str
|
||||
stop:
|
||||
description:
|
||||
- command that is started to stop the main program.
|
||||
required: false
|
||||
type: str
|
||||
type:
|
||||
choices:
|
||||
- simple
|
||||
|
|
|
@ -3,21 +3,21 @@ import pathlib
|
|||
from typing import List, Union
|
||||
|
||||
try:
|
||||
from ansible_collections.sebastian.systemd.plugins.module_utils.generic import SYSTEMD_SERVICE_CONFIG, Types, systemdbool
|
||||
from ansible_collections.sebastian.systemd.plugins.module_utils.module import SystemdReloadMixin, SystemdUnitModule, installable
|
||||
from ansible_module.module_utils.generic import SYSTEMD_SERVICE_CONFIG, Types, systemdbool, modspec # type: ignore[reportMissingImports]
|
||||
from ansible_module.module_utils.module import SystemdReloadMixin, SystemdUnitModule, installable # type: ignore[reportMissingImports]
|
||||
except ImportError:
|
||||
from plugins.module_utils.generic import SYSTEMD_SERVICE_CONFIG, Types, systemdbool
|
||||
from plugins.module_utils.module import SystemdReloadMixin, SystemdUnitModule, installable
|
||||
from ansible_collections.sebastian.base.plugins.module_utils.generic import SYSTEMD_SERVICE_CONFIG, Types, systemdbool, modspec
|
||||
from ansible_collections.sebastian.base.plugins.module_utils.module import SystemdReloadMixin, SystemdUnitModule, installable
|
||||
|
||||
__module_name__ = "TargetModule"
|
||||
|
||||
|
||||
@installable
|
||||
class TargetModule(SystemdUnitModule, SystemdReloadMixin):
|
||||
class TargetModule(SystemdUnitModule, SystemdReloadMixin): # type: ignore[misc]
|
||||
"""Creates Target units"""
|
||||
|
||||
name = "target"
|
||||
module_spec = dict(
|
||||
module_spec = modspec(
|
||||
argument_spec=dict(
|
||||
description=Types.str(help="description of the target"),
|
||||
name=Types.str(required=True, help="name of the target"),
|
||||
|
@ -27,6 +27,7 @@ class TargetModule(SystemdUnitModule, SystemdReloadMixin):
|
|||
),
|
||||
),
|
||||
)
|
||||
restartable = False
|
||||
|
||||
def prepare(self):
|
||||
self.unitfile = (SYSTEMD_SERVICE_CONFIG / self.get("name")).with_suffix(".target")
|
||||
|
@ -34,21 +35,21 @@ class TargetModule(SystemdUnitModule, SystemdReloadMixin):
|
|||
|
||||
def header(self) -> str:
|
||||
section = super().header()
|
||||
if section is None:
|
||||
section = "[Unit]\n"
|
||||
section += "AllowIsolate={}\n".format(systemdbool(self.get("allow_isolate", False)))
|
||||
return section
|
||||
|
||||
def unit(self) -> str:
|
||||
if self.__unit is None:
|
||||
self.__unit = "\n".join(
|
||||
(
|
||||
self.header(),
|
||||
self.install(),
|
||||
)
|
||||
self.__unit = self._unit(
|
||||
self.header(),
|
||||
self.install(), # type: ignore[call-arg,misc]
|
||||
)
|
||||
return self.__unit
|
||||
|
||||
|
||||
DOCUMENTATION = """---
|
||||
DOCUMENTATION = r"""---
|
||||
description:
|
||||
- Creates Target units
|
||||
module: target
|
||||
|
|
252
plugins/modules/timer.py
Normale Datei
252
plugins/modules/timer.py
Normale Datei
|
@ -0,0 +1,252 @@
|
|||
#!/usr/bin/python3
|
||||
import pathlib
|
||||
from typing import List, Union
|
||||
|
||||
try:
|
||||
from ansible_module.module_utils.generic import SYSTEMD_SERVICE_CONFIG, Types, modspec # type: ignore[reportMissingImports]
|
||||
from ansible_module.module_utils.module import SystemdReloadMixin, SystemdUnitModule, installable # type: ignore[reportMissingImports]
|
||||
except ImportError:
|
||||
from ansible_collections.sebastian.base.plugins.module_utils.generic import SYSTEMD_SERVICE_CONFIG, Types, modspec
|
||||
from ansible_collections.sebastian.base.plugins.module_utils.module import SystemdReloadMixin, SystemdUnitModule, installable
|
||||
|
||||
|
||||
__module_name__ = "TimerModule"
|
||||
|
||||
|
||||
@installable
|
||||
class TimerModule(SystemdUnitModule, SystemdReloadMixin): # type: ignore[misc]
|
||||
"""Creates Timer units"""
|
||||
|
||||
name = "timer"
|
||||
module_spec = modspec(
|
||||
argument_spec=dict(
|
||||
name=Types.str(required=True, help="Name of the unit"),
|
||||
description=Types.str(help="Description of the timer"),
|
||||
onactive=Types.list(
|
||||
help="Starts the service x seconds after this timer was activated",
|
||||
elements=Types.str(help="Takes the time in seconds or an timespan like '5m 20s'"),
|
||||
),
|
||||
onboot=Types.list(
|
||||
help="Starts the service x seconds after the device was booted or the container was started",
|
||||
elements=Types.str(help="Takes the time in seconds or an timespan like '5m 20s'"),
|
||||
),
|
||||
onstartup=Types.list(
|
||||
help="Starts the service x seconds after the System's/User's systemd instance was started",
|
||||
elements=Types.str(help="Takes the time in seconds or an timespan like '5m 20s'"),
|
||||
),
|
||||
onunitactive=Types.list(
|
||||
help="Starts the service x seconds after the unit this timer activates was last activated",
|
||||
elements=Types.str(help="Takes the time in seconds or an timespan like '5m 20s'"),
|
||||
),
|
||||
onunitinactive=Types.list(
|
||||
help="Starts the service x seconds after the unit this timer activates was last deactivated",
|
||||
elements=Types.str(help="Takes the time in seconds or an timespan like '5m 20s'"),
|
||||
),
|
||||
oncalendar=Types.list(
|
||||
help="Uses an Time string to start the unit.",
|
||||
elements=Types.str(help="Takes an Value representing a specific date or dates"),
|
||||
),
|
||||
persistent=Types.bool(
|
||||
help="If the system was down in the time the timer would have started the unit, start the unit as soon as possible."
|
||||
),
|
||||
randomdelay=Types.str(help="delays the activation by an random delay between 0 and the value"),
|
||||
fixdelay=Types.bool(
|
||||
help="set the random delay to an fixed value. It uses the timername, the user of the servicemanager and the machineid as the seed."
|
||||
),
|
||||
unit=Types.str(help="The name of the unit. only needed if its not {{name}}.service"),
|
||||
),
|
||||
required_one_of=[
|
||||
(
|
||||
"onactive",
|
||||
"onboot",
|
||||
"onstartup",
|
||||
"onunitactive",
|
||||
"onunitinactive",
|
||||
"oncalendar",
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
def prepare(self):
|
||||
self.unitfile = (SYSTEMD_SERVICE_CONFIG / self.get("name")).with_suffix(".timer")
|
||||
self.__unit = None
|
||||
|
||||
def body(self):
|
||||
section = "[Timer]\n"
|
||||
params = []
|
||||
params.extend(
|
||||
self.map_param(
|
||||
onactive="OnActiveSec",
|
||||
onboot="OnBootSec",
|
||||
onstartup="OnStartupSec",
|
||||
onunitactive="OnUnitActiveSec",
|
||||
onunitinactive="OnUnitInactiveSec",
|
||||
oncalendar="OnCalendar",
|
||||
persistent="Persistent",
|
||||
randomdelay="RandomizedDelaySec",
|
||||
fixdelay="FixedRandomDelay",
|
||||
unit="Unit",
|
||||
),
|
||||
)
|
||||
if len(params) == 0:
|
||||
return None
|
||||
section += "".join(params)
|
||||
return section
|
||||
|
||||
def unit(self) -> str:
|
||||
if self.__unit is None:
|
||||
self.__unit = self._unit(
|
||||
self.header(),
|
||||
self.body(),
|
||||
self.install(), # type: ignore[call-arg,misc]
|
||||
)
|
||||
return self.__unit
|
||||
|
||||
|
||||
DOCUMENTATION = r"""---
|
||||
description:
|
||||
- Creates Timer units
|
||||
module: timer
|
||||
options:
|
||||
after:
|
||||
default: []
|
||||
description:
|
||||
- list of units that this unit wants to be started after this unit
|
||||
elements: str
|
||||
required: false
|
||||
type: list
|
||||
before:
|
||||
default: []
|
||||
description:
|
||||
- list of units that this unit needs to be started before this unit.
|
||||
elements: str
|
||||
required: false
|
||||
type: list
|
||||
description:
|
||||
description:
|
||||
- Description of the timer
|
||||
required: false
|
||||
type: str
|
||||
documentation:
|
||||
default: []
|
||||
description:
|
||||
- Paths where documentation can be found
|
||||
elements: str
|
||||
required: false
|
||||
type: list
|
||||
fixdelay:
|
||||
description:
|
||||
- set the random delay to an fixed value. It uses the timername, the user of the
|
||||
servicemanager and the machineid as the seed.
|
||||
required: false
|
||||
type: bool
|
||||
name:
|
||||
description:
|
||||
- Name of the unit
|
||||
required: true
|
||||
type: str
|
||||
onactive:
|
||||
default: []
|
||||
description:
|
||||
- Starts the service x seconds after this timer was activated
|
||||
elements: str
|
||||
required: false
|
||||
type: list
|
||||
onboot:
|
||||
default: []
|
||||
description:
|
||||
- Starts the service x seconds after the device was booted or the container was
|
||||
started
|
||||
elements: str
|
||||
required: false
|
||||
type: list
|
||||
oncalendar:
|
||||
default: []
|
||||
description:
|
||||
- Uses an Time string to start the unit.
|
||||
elements: str
|
||||
required: false
|
||||
type: list
|
||||
onstartup:
|
||||
default: []
|
||||
description:
|
||||
- Starts the service x seconds after the System's/User's systemd instance was
|
||||
started
|
||||
elements: str
|
||||
required: false
|
||||
type: list
|
||||
onunitactive:
|
||||
default: []
|
||||
description:
|
||||
- Starts the service x seconds after the unit this timer activates was last activated
|
||||
elements: str
|
||||
required: false
|
||||
type: list
|
||||
onunitinactive:
|
||||
default: []
|
||||
description:
|
||||
- Starts the service x seconds after the unit this timer activates was last deactivated
|
||||
elements: str
|
||||
required: false
|
||||
type: list
|
||||
partof:
|
||||
default: []
|
||||
description:
|
||||
- list of units that this unit is part of.
|
||||
- If the restart this unit does it too, but if this restarts it does not affect
|
||||
the other units.
|
||||
elements: str
|
||||
required: false
|
||||
type: list
|
||||
persistent:
|
||||
description:
|
||||
- If the system was down in the time the timer would have started the unit, start
|
||||
the unit as soon as possible.
|
||||
required: false
|
||||
type: bool
|
||||
randomdelay:
|
||||
description:
|
||||
- delays the activation by an random delay between 0 and the value
|
||||
required: false
|
||||
type: str
|
||||
required_by:
|
||||
default: []
|
||||
description:
|
||||
- systemd units that require this mount
|
||||
elements: str
|
||||
required: false
|
||||
type: list
|
||||
requires:
|
||||
default: []
|
||||
description:
|
||||
- list of units that this unit requires. If it fails or can't be started this
|
||||
unit fails. without before/after this is started at the same time
|
||||
elements: str
|
||||
required: false
|
||||
type: list
|
||||
unit:
|
||||
description:
|
||||
- The name of the unit. only needed if its not {{name}}.service
|
||||
required: false
|
||||
type: str
|
||||
wanted_by:
|
||||
default: []
|
||||
description:
|
||||
- systemd units that want the mount, but not explicitly require it. Commonly used
|
||||
for target if not service explicitly require it.
|
||||
elements: str
|
||||
required: false
|
||||
type: list
|
||||
wants:
|
||||
default: []
|
||||
description:
|
||||
- list of units that this unit wants. If it fails or can't be started it does
|
||||
not affect this unit
|
||||
elements: str
|
||||
required: false
|
||||
type: list
|
||||
short_description: Creates Timer units
|
||||
"""
|
||||
|
||||
if __name__ == "__main__":
|
||||
TimerModule()()
|
|
@ -3,19 +3,23 @@ import pathlib
|
|||
from typing import List, Union
|
||||
|
||||
try:
|
||||
from ansible_collections.sebastian.systemd.plugins.module_utils.generic import SYSTEMD_SERVICE_CONFIG, Types, modpec
|
||||
from ansible_collections.sebastian.systemd.plugins.module_utils.module import SystemdReloadMixin, SystemdUnitModule, installable
|
||||
from ansible_module.module_utils.generic import SYSTEMD_SERVICE_CONFIG, Types, modspec
|
||||
from ansible_module.module_utils.module import SystemdReloadMixin, SystemdUnitModule, installable
|
||||
except ImportError:
|
||||
from plugins.module_utils.generic import SYSTEMD_SERVICE_CONFIG, Types, modspec
|
||||
from plugins.module_utils.module import SystemdReloadMixin, SystemdUnitModule, installable
|
||||
from ansible_collections.sebastian.base.plugins.module_utils.generic import SYSTEMD_SERVICE_CONFIG, Types, modspec
|
||||
from ansible_collections.sebastian.base.plugins.module_utils.module import SystemdReloadMixin, SystemdUnitModule, installable
|
||||
|
||||
|
||||
@installable
|
||||
class Module(SystemdUnitModule, SystemdReloadMixin): # type: ignore
|
||||
class Module(SystemdUnitModule, SystemdReloadMixin): # type: ignore
|
||||
"""Creates units"""
|
||||
|
||||
name = "unit"
|
||||
module_spec = modspec(argument_spec=dict(name=Types.str(required=True, help="Name of the unit"),))
|
||||
module_spec = modspec(
|
||||
argument_spec=dict(
|
||||
name=Types.str(required=True, help="Name of the unit"),
|
||||
)
|
||||
)
|
||||
|
||||
def prepare(self):
|
||||
self.unitfile = (SYSTEMD_SERVICE_CONFIG / self.get("name")).with_suffix(".")
|
||||
|
|
|
@ -4,4 +4,7 @@ line-length = 140
|
|||
[tool.isort]
|
||||
atomic = true
|
||||
profile = "black"
|
||||
line_length = 140
|
||||
line_length = 140
|
||||
|
||||
[tool.mypy]
|
||||
disable_error_code = ["import-untyped", "no-redef", "attr-defined"]
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
import os
|
||||
import unittest
|
||||
|
||||
try: # pragma: nocover
|
||||
from ansible_collections.sebastian.systemd.plugins.module_utils.generic import Types
|
||||
except ImportError: # pragma: nocover
|
||||
import sys
|
||||
|
||||
sys.path.append("plugins/module_utils")
|
||||
from generic import Types
|
||||
|
||||
|
||||
class TestTypes(unittest.TestCase):
|
||||
"""tests the Types class"""
|
||||
|
||||
def testsimpletype(self):
|
||||
"""this tests if an simple type is correctly build"""
|
||||
output = Types.str(required=True, help="test", choices=("a", "1"), default="1")
|
||||
self.assertIn("type", output)
|
||||
self.assertIn("required", output)
|
||||
self.assertIn("default", output)
|
||||
self.assertIn("choices", output)
|
||||
self.assertEquals(output["type"], "str")
|
||||
self.assertEquals(Types.str.__name__, "str")
|
||||
self.assertEquals(output["required"], True)
|
||||
self.assertEquals(output["default"], "1")
|
||||
self.assertTupleEqual(output["choices"], ("a", "1"))
|
||||
Types.str()
|
||||
|
||||
def testlisttype(self):
|
||||
"""this tests if the special type list is correctly build"""
|
||||
output = Types.list(str)
|
||||
Types.list("str")
|
||||
self.assertIn("type", output)
|
||||
self.assertIn("elements", output)
|
||||
self.assertIn("required", output)
|
||||
self.assertEquals(output["type"], "list")
|
||||
self.assertEquals(output["required"], False)
|
||||
self.assertEquals(output["elements"], "str")
|
41
update_doc
41
update_doc
|
@ -1,41 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
import importlib
|
||||
import pathlib
|
||||
import re
|
||||
import sys
|
||||
|
||||
sys.path.append(".")
|
||||
mindocstring = "DOCUMENTATION = ''''''"
|
||||
moduledir = pathlib.Path("plugins/modules")
|
||||
regex = re.compile("DOCUMENTATION *= *r?(?P<quote>\"{3}|'{3})(---)?.*?(?P=quote)", re.MULTILINE | re.DOTALL)
|
||||
|
||||
if __name__ == "__main__":
|
||||
for modfile in moduledir.iterdir():
|
||||
if modfile.name in ( "__init__.py", "__pycache__", "unit.py.example"):
|
||||
continue
|
||||
mod = importlib.import_module(".".join((modfile.parts[:-1])+(modfile.stem,)))
|
||||
if hasattr(mod, "Module"):
|
||||
module = mod.Module
|
||||
elif hasattr(mod, "__module_name__"):
|
||||
module = getattr(mod, mod.__module_name__)
|
||||
else:
|
||||
print("Error loading Module of File: {}. No Module or __module_name__ defined".format(modfile))
|
||||
continue
|
||||
try:
|
||||
moddoc = module.doc()
|
||||
except AttributeError:
|
||||
print("Broken module. skipping {}".format(modfile))
|
||||
continue
|
||||
except Exception as e:
|
||||
print("Error in documentation of module {}: {}".format(modfile, e))
|
||||
continue
|
||||
moddata = modfile.read_text()
|
||||
match = regex.search(moddata)
|
||||
if not match:
|
||||
print("no Documentation set for module {}. Please add at least \"{}\" to the file".format(modfile.stem, mindocstring))
|
||||
continue
|
||||
newmod = "{pre}DOCUMENTATION = {quote}{doc}{quote}{post}".format(pre=moddata[:match.start()], quote=match.group("quote"), doc=moddoc, post=moddata[match.end():])
|
||||
modfile.write_text(newmod)
|
||||
print("updated the documentation of module {}".format(module.name))
|
||||
|
||||
# code: python
|
6
upload.sh
Ausführbare Datei
6
upload.sh
Ausführbare Datei
|
@ -0,0 +1,6 @@
|
|||
#!/bin/bash
|
||||
user=$(yq -r .namespace galaxy.yml)
|
||||
package=$(yq -r .name galaxy.yml)
|
||||
version=$(yq -r .version galaxy.yml)
|
||||
printf "Namespace: %s\nPackage: %s\nVersion: %s\nfile name: %s\n" $user $package $version "$user-$package-$version.tar.gz"
|
||||
ansible-galaxy collection publish dist/*
|
Laden …
Tabelle hinzufügen
In neuem Issue referenzieren