created an ansible module with module utils
Dieser Commit ist enthalten in:
Ursprung
5b9dd8df0b
Commit
768713aadc
|
@ -0,0 +1,14 @@
|
|||
============================
|
||||
Sebastian.Base Release Notes
|
||||
============================
|
||||
|
||||
.. contents:: Topics
|
||||
|
||||
|
||||
v0.2.0
|
||||
======
|
||||
|
||||
Release Summary
|
||||
---------------
|
||||
|
||||
change the module to an ansible module
|
|
@ -0,0 +1,21 @@
|
|||
format:
|
||||
black .
|
||||
isort .
|
||||
|
||||
changelog:
|
||||
antsibull-changelog generate
|
||||
|
||||
docs: format
|
||||
update-doc
|
||||
|
||||
clean-dist:
|
||||
rm -rf dist
|
||||
|
||||
hatch-release:
|
||||
hatch build
|
||||
|
||||
galaxy-release: clean-dist changelog
|
||||
ansible-galaxy collection build --output-path dist
|
||||
|
||||
upload: galaxy-release hatch-release
|
||||
./upload.sh
|
19
README.md
19
README.md
|
@ -1,18 +1,3 @@
|
|||
# Ansible module
|
||||
# Ansible Collection - sebastian.base
|
||||
|
||||
-----
|
||||
|
||||
**Table of Contents**
|
||||
|
||||
- [Installation](#installation)
|
||||
- [License](#license)
|
||||
|
||||
## Installation
|
||||
|
||||
```console
|
||||
pip install ansible-module
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
`ansible-module` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license.
|
||||
Documentation for the collection.
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
objects:
|
||||
role: {}
|
||||
plugins:
|
||||
become: {}
|
||||
cache: {}
|
||||
callback: {}
|
||||
cliconf: {}
|
||||
connection: {}
|
||||
filter: {}
|
||||
httpapi: {}
|
||||
inventory: {}
|
||||
lookup: {}
|
||||
module: {}
|
||||
netconf: {}
|
||||
shell: {}
|
||||
strategy: {}
|
||||
test: {}
|
||||
vars: {}
|
||||
version: 0.2.0
|
|
@ -0,0 +1,8 @@
|
|||
ancestor: null
|
||||
releases:
|
||||
0.2.0:
|
||||
changes:
|
||||
release_summary: change the module to an ansible module
|
||||
fragments:
|
||||
- base_release.yml
|
||||
release_date: '2024-02-11'
|
|
@ -0,0 +1,32 @@
|
|||
changelog_filename_template: ../CHANGELOG.rst
|
||||
changelog_filename_version_depth: 0
|
||||
changes_file: changelog.yaml
|
||||
changes_format: combined
|
||||
ignore_other_fragment_extensions: true
|
||||
keep_fragments: false
|
||||
mention_ancestor: true
|
||||
new_plugins_after_name: removed_features
|
||||
notesdir: fragments
|
||||
prelude_section_name: release_summary
|
||||
prelude_section_title: Release Summary
|
||||
sanitize_changelog: true
|
||||
sections:
|
||||
- - major_changes
|
||||
- Major Changes
|
||||
- - minor_changes
|
||||
- Minor Changes
|
||||
- - breaking_changes
|
||||
- Breaking Changes / Porting Guide
|
||||
- - deprecated_features
|
||||
- Deprecated Features
|
||||
- - removed_features
|
||||
- Removed Features (previously deprecated)
|
||||
- - security_fixes
|
||||
- Security Fixes
|
||||
- - bugfixes
|
||||
- Bugfixes
|
||||
- - known_issues
|
||||
- Known Issues
|
||||
title: Sebastian.Base
|
||||
trivial_section_name: trivial
|
||||
use_fqcn: true
|
|
@ -0,0 +1,34 @@
|
|||
---
|
||||
namespace: sebastian
|
||||
name: base
|
||||
version: 0.2.0
|
||||
|
||||
readme: README.md
|
||||
|
||||
authors:
|
||||
- Sebastian Tobie
|
||||
description: >
|
||||
The base of my ansible collections. It provides the nessesary tools for my modules
|
||||
license_file: LICENSE
|
||||
tags:
|
||||
- linux
|
||||
- systemd
|
||||
dependencies: {}
|
||||
|
||||
repository: https://gitea.sebastian-tobie.de/ansible/ansible-module.git
|
||||
# documentation: https://gitea.sebastian-tobie.de/ansible/ansible-module
|
||||
homepage: https://gitea.sebastian-tobie.de/ansible/ansible-module
|
||||
issues: https://gitea.sebastian-tobie.de/ansible/ansible-module/issues
|
||||
|
||||
build_ignore:
|
||||
- "*.gz"
|
||||
- ".*"
|
||||
- Makefile
|
||||
- pyproject.toml
|
||||
- upload.sh
|
||||
- htmlcov
|
||||
- changelogs
|
||||
- docs
|
||||
- src
|
||||
- "coverage.*"
|
||||
- dist
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
# Collections must specify a minimum required ansible version to upload
|
||||
# to galaxy
|
||||
requires_ansible: '>=2.9.10'
|
|
@ -0,0 +1,31 @@
|
|||
# Collections Plugins Directory
|
||||
|
||||
This directory can be used to ship various plugins inside an Ansible collection. Each plugin is placed in a folder that
|
||||
is named after the type of plugin it is in. It can also include the `module_utils` and `modules` directory that
|
||||
would contain module utils and modules respectively.
|
||||
|
||||
Here is an example directory of the majority of plugins currently supported by Ansible:
|
||||
|
||||
```
|
||||
└── plugins
|
||||
├── action
|
||||
├── become
|
||||
├── cache
|
||||
├── callback
|
||||
├── cliconf
|
||||
├── connection
|
||||
├── filter
|
||||
├── httpapi
|
||||
├── inventory
|
||||
├── lookup
|
||||
├── module_utils
|
||||
├── modules
|
||||
├── netconf
|
||||
├── shell
|
||||
├── strategy
|
||||
├── terminal
|
||||
├── test
|
||||
└── vars
|
||||
```
|
||||
|
||||
A full list of plugin types can be found at [Working With Plugins](https://docs.ansible.com/ansible-core/2.16/plugins/plugins.html).
|
|
@ -5,7 +5,7 @@ from typing import (Any, Callable, ClassVar, Dict, NoReturn, Optional, Type,
|
|||
|
||||
import ansible.module_utils.basic as basic
|
||||
|
||||
from ansible_module.generic import AnsibleParameter, Types, systemdbool
|
||||
from .generic import AnsibleParameter, Types, systemdbool
|
||||
|
||||
__all__ = (
|
||||
"AnsibleModule",
|
|
@ -32,6 +32,9 @@ Source = "https://github.com/unknown/ansible-module"
|
|||
[project.scripts]
|
||||
update-doc = "ansible_module.update_doc:main"
|
||||
|
||||
[tool.hatch.build.targets.wheel.force-include]
|
||||
"plugins/module_utils" = "ansible_module/module_utils"
|
||||
|
||||
[tool.hatch.version]
|
||||
path = "src/ansible_module/__init__.py"
|
||||
|
||||
|
@ -135,7 +138,12 @@ ansible_module = ["src/ansible_module", "*/ansible-module/src/ansible_module"]
|
|||
tests = ["tests", "*/ansible-module/tests"]
|
||||
|
||||
[tool.coverage.report]
|
||||
exclude_lines = ["no cov", "if __name__ == .__main__.:", "if TYPE_CHECKING:", "pragma: nocover"]
|
||||
exclude_lines = [
|
||||
"no cov",
|
||||
"if __name__ == .__main__.:",
|
||||
"if TYPE_CHECKING:",
|
||||
"pragma: nocover",
|
||||
]
|
||||
|
||||
[tool.mypy]
|
||||
python_version = "3.11"
|
||||
|
@ -148,13 +156,11 @@ ignore_missing_imports = true
|
|||
|
||||
[tool.pytest.ini_options]
|
||||
addopts = [
|
||||
"--cov-report", "json",
|
||||
"--cov-report", "term-missing:skip-covered",
|
||||
"--cov-report",
|
||||
"json",
|
||||
"--cov-report",
|
||||
"term-missing:skip-covered",
|
||||
"--cov",
|
||||
]
|
||||
pythonpath = ["src"]
|
||||
required_plugins = [
|
||||
"pytest-cov",
|
||||
"pytest-isort",
|
||||
"pytest-mypy"
|
||||
]
|
||||
required_plugins = ["pytest-cov", "pytest-isort", "pytest-mypy"]
|
||||
|
|
|
@ -1 +1 @@
|
|||
__version__ = "0.1.3"
|
||||
__version__ = "0.2.0"
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
from . import update_doc
|
||||
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
update_doc.main()
|
||||
|
|
|
@ -1,76 +0,0 @@
|
|||
from typing import Any, Dict, Optional, Type, Union
|
||||
|
||||
from _typeshed import Incomplete
|
||||
|
||||
SYSTEMD_CONFIG_ROOT: Incomplete
|
||||
SYSTEMD_NETWORK_CONFIG: Incomplete
|
||||
SYSTEMD_SERVICE_CONFIG: Incomplete
|
||||
AnsibleParameter = Dict[str, Any]
|
||||
|
||||
class Types:
|
||||
def list(
|
||||
elements: Union[Type[object], str, AnsibleParameter], required: bool = False, help: Optional[str] = None
|
||||
) -> AnsibleParameter: ...
|
||||
def dict(required: bool = False, help: Optional[str] = None, **options: dict) -> AnsibleParameter: ...
|
||||
def str(
|
||||
required: bool = False,
|
||||
help: Optional[str] = None,
|
||||
choices: Optional[Sequence] = None,
|
||||
default: Optional[Any] = None,
|
||||
) -> AnsibleParameter: ...
|
||||
def bool(
|
||||
required: bool = False,
|
||||
help: Optional[str] = None,
|
||||
choices: Optional[Sequence] = None,
|
||||
default: Optional[Any] = None,
|
||||
) -> AnsibleParameter: ...
|
||||
def int(
|
||||
required: bool = False,
|
||||
help: Optional[str] = None,
|
||||
choices: Optional[Sequence] = None,
|
||||
default: Optional[Any] = None,
|
||||
) -> AnsibleParameter: ...
|
||||
def float(
|
||||
required: bool = False,
|
||||
help: Optional[str] = None,
|
||||
choices: Optional[Sequence] = None,
|
||||
default: Optional[Any] = None,
|
||||
) -> AnsibleParameter: ...
|
||||
def path(
|
||||
required: bool = False,
|
||||
help: Optional[str] = None,
|
||||
choices: Optional[Sequence] = None,
|
||||
default: Optional[Any] = None,
|
||||
) -> AnsibleParameter: ...
|
||||
def raw(
|
||||
required: bool = False,
|
||||
help: Optional[str] = None,
|
||||
choices: Optional[Sequence] = None,
|
||||
default: Optional[Any] = None,
|
||||
) -> AnsibleParameter: ...
|
||||
def jsonarg(
|
||||
required: bool = False,
|
||||
help: Optional[str] = None,
|
||||
choices: Optional[Sequence] = None,
|
||||
default: Optional[Any] = None,
|
||||
) -> AnsibleParameter: ...
|
||||
def json(
|
||||
required: bool = False,
|
||||
help: Optional[str] = None,
|
||||
choices: Optional[Sequence] = None,
|
||||
default: Optional[Any] = None,
|
||||
) -> AnsibleParameter: ...
|
||||
def bytes(
|
||||
required: bool = False,
|
||||
help: Optional[str] = None,
|
||||
choices: Optional[Sequence] = None,
|
||||
default: Optional[Any] = None,
|
||||
) -> AnsibleParameter: ...
|
||||
def bits(
|
||||
required: bool = False,
|
||||
help: Optional[str] = None,
|
||||
choices: Optional[Sequence] = None,
|
||||
default: Optional[Any] = None,
|
||||
) -> AnsibleParameter: ...
|
||||
|
||||
def systemdbool(b: Union[bool, str]) -> str: ...
|
|
@ -1,60 +0,0 @@
|
|||
import pathlib
|
||||
from typing import (Any, Callable, ClassVar, Dict, NoReturn, Optional, Type,
|
||||
TypeVar, overload)
|
||||
|
||||
import ansible.module_utils.basic as basic
|
||||
from _typeshed import Incomplete
|
||||
|
||||
T = TypeVar('T')
|
||||
|
||||
class AnsibleModule:
|
||||
name: ClassVar[str]
|
||||
module: basic.AnsibleModule
|
||||
result: dict
|
||||
module_spec: ClassVar[dict]
|
||||
@property
|
||||
def params(self) -> Dict[str, Any]: ...
|
||||
tmpdir: Incomplete
|
||||
def __init__(self) -> None: ...
|
||||
def set(self, key: str, value): ...
|
||||
@overload
|
||||
def diff(self, diff: Dict[str, str]): ...
|
||||
@overload
|
||||
def diff(
|
||||
self,
|
||||
before: Optional[str] = ...,
|
||||
after: Optional[str] = ...,
|
||||
before_header: Optional[str] = ...,
|
||||
after_header: Optional[str] = ...,
|
||||
): ...
|
||||
def get(self, key: str, default: Optional[T] = ...) -> T: ...
|
||||
def changed_get(self): ...
|
||||
def changed_set(self, value) -> None: ...
|
||||
changed: Incomplete
|
||||
def prepare(self) -> None: ...
|
||||
def check(self) -> None: ...
|
||||
def run(self) -> None: ...
|
||||
def __call__(self) -> NoReturn: ...
|
||||
@classmethod
|
||||
def doc(cls) -> str: ...
|
||||
|
||||
class SystemdUnitModule(AnsibleModule):
|
||||
unitfile: pathlib.Path
|
||||
post: Optional[Callable[[], None]]
|
||||
install: ClassVar[Optional[Callable[[SystemdUnitModule], str]]]
|
||||
def unit(self) -> str: ...
|
||||
def header(self) -> str: ...
|
||||
def map_param(self, **parammap: str): ...
|
||||
changed: Incomplete
|
||||
def unitfile_gen(self) -> None: ...
|
||||
def check(self) -> None: ...
|
||||
def run(self) -> None: ...
|
||||
|
||||
def installable(_class: Type[SystemdUnitModule]): ...
|
||||
|
||||
class SystemdReloadMixin:
|
||||
module: basic.AnsibleModule
|
||||
unitfile: pathlib.Path
|
||||
restartable: bool
|
||||
changed: bool
|
||||
def post(self) -> None: ...
|
|
@ -1,3 +0,0 @@
|
|||
# SPDX-FileCopyrightText: 2023-present Sebastian Tobie <sebastian@sebastian-tobie.de>
|
||||
#
|
||||
# SPDX-License-Identifier: MIT
|
|
@ -1,57 +0,0 @@
|
|||
import os
|
||||
import unittest
|
||||
|
||||
from ansible_module.generic import Types, joindict, systemdbool
|
||||
|
||||
|
||||
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.assertEqual(output["type"], "str")
|
||||
self.assertEqual(Types.str.__name__, "str")
|
||||
self.assertEqual(output["required"], True)
|
||||
self.assertEqual(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.assertEqual(output["type"], "list")
|
||||
self.assertEqual(output["required"], False)
|
||||
self.assertEqual(output["elements"], "str")
|
||||
Types.list(Types.dict(a=Types.str(help="")), help="")
|
||||
Types.list("str")
|
||||
|
||||
def testdicttype(self):
|
||||
output = Types.dict(help="HILFE")
|
||||
self.assertIn("type", output)
|
||||
self.assertIn("required", output)
|
||||
self.assertIn("description", output)
|
||||
self.assertIn("option", output)
|
||||
|
||||
|
||||
class TestFuncs(unittest.TestCase):
|
||||
def testsystemdbool(self):
|
||||
self.assertEqual("Text", systemdbool("Text"))
|
||||
self.assertEqual("no", systemdbool(False))
|
||||
self.assertEqual("yes", systemdbool(True))
|
||||
|
||||
def testjoindict(self):
|
||||
dicts = (
|
||||
dict(a=1, b=2),
|
||||
dict(b=3, c=4),
|
||||
)
|
||||
output = dict(a=1, b=3, c=4)
|
||||
self.assertDictEqual(output, joindict(*dicts))
|
|
@ -1,71 +0,0 @@
|
|||
import sys
|
||||
import unittest
|
||||
|
||||
from ansible_module.generic import modspec
|
||||
from ansible_module.module import SystemdUnitModule, docify
|
||||
|
||||
|
||||
class ModuleMock(SystemdUnitModule):
|
||||
name = "mock"
|
||||
module_spec = modspec({})
|
||||
|
||||
|
||||
class TestSystemdUnitModule(unittest.TestCase):
|
||||
def setUp(self):
|
||||
sys.argv = [sys.argv[0], '{"ANSIBLE_MODULE_ARGS":{}}']
|
||||
self.mod = ModuleMock()
|
||||
|
||||
def test_header(self):
|
||||
self.assertEqual("[Unit]\n", self.mod.header())
|
||||
|
||||
def test_doc(self):
|
||||
ModuleMock.doc()
|
||||
|
||||
def test_params(self):
|
||||
self.mod.module.params = dict(ok="ok", none=None, true=True, false=False, list=("item1", "item2"))
|
||||
self.assertEqual("ok", self.mod.get("ok"))
|
||||
self.assertEqual("default", self.mod.get("none", "default"))
|
||||
self.assertRaises(KeyError, self.mod.get, "none", None)
|
||||
self.assertListEqual(
|
||||
['TRUE=yes\n', "FALSE=no\n", "OK=ok\n", "LIST=item1\n", "LIST=item2\n"],
|
||||
self.mod.map_param(true="TRUE", false="FALSE", ok="OK", list="LIST"),
|
||||
)
|
||||
self.assertRaises(TypeError, self.mod.diff, diff=dict(), before=True, after=False)
|
||||
self.mod.diff(before="a", after="b", before_header="a", after_header="b")
|
||||
self.mod.diff(dict(before="c", after="d"))
|
||||
|
||||
def test_result(self):
|
||||
self.assertFalse(self.mod.changed)
|
||||
self.mod.changed = 5
|
||||
self.assertTrue(self.mod.changed)
|
||||
self.mod.set("key", "value")
|
||||
self.assertDictEqual(
|
||||
dict(
|
||||
changed=True,
|
||||
key="value",
|
||||
),
|
||||
self.mod.result,
|
||||
)
|
||||
|
||||
|
||||
class TestFunctions(unittest.TestCase):
|
||||
def test_docify(self):
|
||||
input = dict(
|
||||
item1=dict(type="str", description=""),
|
||||
item2=dict(required=True, type="int", default=5),
|
||||
item3=dict(type="list", required=False, elements="int"),
|
||||
item4=dict(type="list", required=True, elements="int"),
|
||||
item5=dict(type="str", choices=["a", "b", "c"]),
|
||||
item6=dict(type="dict", options=dict(a=dict(type="str"))),
|
||||
)
|
||||
expected = dict(
|
||||
item1=dict(required=False, type="str", description=[""]),
|
||||
item2=dict(required=True, type="int", default=5),
|
||||
item3=dict(type="list", required=False, elements="int", default=[]),
|
||||
item4=dict(type="list", required=True, elements="int"),
|
||||
item5=dict(type="str", required=False, choices=("a", "b", "c")),
|
||||
item6=dict(type="dict", required=False, options=dict(a=dict(type="str", required=False))),
|
||||
)
|
||||
output = docify(input)
|
||||
for key, value in input.items():
|
||||
self.assertDictEqual(expected[key], output[key])
|
|
@ -0,0 +1,13 @@
|
|||
#!/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\n" $user $package $version
|
||||
upload(){
|
||||
name=$(basename "$1")
|
||||
printf "uploading: %s as %s\n" "$1" "$name"
|
||||
curl -u sebastian --upload-file "$1" "https://gitea.sebastian-tobie.de/api/packages/ansible/generic/${package}/${version}/$name"
|
||||
}
|
||||
for file in dist/* ; do
|
||||
upload "$file"
|
||||
done
|
Laden…
In neuem Issue referenzieren