diff --git a/docs/moddev/overview.rst b/docs/moddev/overview.rst index fbd05c54..11b2860c 100644 --- a/docs/moddev/overview.rst +++ b/docs/moddev/overview.rst @@ -10,6 +10,7 @@ Modules Development proto modstruct + pymod Overview -------- @@ -20,19 +21,38 @@ concerns: - Unpredictable environments can require different runtime constraints - It must be as simple as possible to extend SysInspect with own custom modules, enabling unequal programming skill levels -Therefore, Modules for SysInspect are basically a standalone programs on their own, communicating +There are two kind of purposes to call the modules: + +- System integration assertion +- Applying a system state + +One can use either of these purposes or mix them together. + +Native Modules +-------------- + +Modules for SysInspect are essentially a standalone programs on their own, communicating via protocol in JSON format. Data exchange channel is done via STDIN/STDOUT. One can develop them in any language or scripts, as long as a Module is supporting defined communication protocol. This approach enables everyone to be as flexible and free as possible, adapting to any unpredictable environment and allowing to choose any technology one might like to. -Runtime Modes -============= +Python Modules +-------------- + +.. important:: + + What!? Python? *Without* any extra kind of runtime??.. + +Yes! -There are several ways of running Modules on the system: +Since version 0.2.0, SysInspect brings its own embedded Python runtime, specification 3.12. However, +this runtime comes with the limitations. It contains a "frozen" standard library and does not support +native modules. This means: -- Local -- Over SSH (or in some cases over telnet, remote shell, serial etc) -- Remote passive local, via agent +- Anything which is written in Python supposed to work +- Anything which is native (C or C++) will not work and will never be supported -Each of these modes has their own limitations, advantages, needs and purposes. +On the other hand, the entire Minion with the whole Python runtime and its standard +"included batteries" costs just 29 Mb on the disk and is shipped just as one single static +binary (``musl``). diff --git a/docs/moddev/pymod.rst b/docs/moddev/pymod.rst new file mode 100644 index 00000000..53b71b75 --- /dev/null +++ b/docs/moddev/pymod.rst @@ -0,0 +1,118 @@ +Python Modules +============== + +.. note:: + + This document describes how to develop modules in Python. + +Overview +======== + +Native modules are fine, but not everyone will like to craft them and distribute them +on the constrained embedded environments. For this reasons, SysInspect brings the whole +entire Python 3.12 embedded runtime. + +Installation +============ + +None. As long as SysInspect Minion is running, it is ready to execute any pure Python +code. + +Python Module Tutorial +====================== + +This is a very short self-explanatory Tutorial how to make a Python module for SysInspect. + +Principle +--------- + +Writing Python module is essentially boils down to the same principle as native modules: + +1. Do whatever you need to do +2. Return a proper JSON structure back + +Hello, world! +------------- + +To write a very simple "Hello, world!" module, you will need to use ``main(*args, **kw)`` +function at the module level, which will be called to execute everything. The ``args`` and ``kw`` +is what comes from the Action definition. The rest is totally relying on the module logic. + +A very minimal module would look like so: + +.. code-block:: python + + # 1. Get a return Object from the standard library + from sysinspect import SysinspectReturn + + # 2. Define main function + def main(*args, **kw) -> str: + """ + Main function to dispatch the module. + """ + + # 3. Instantiate return object + r = SysinspectReturn() + + # 4. Add data that needs to be returned back + r.add_data({"hello", "world!"}) + + # 5. Return it as a string, calling str() on that object! + return str(r) + +Module can also take advantage of SysInspect core and use current Minion traits. This way +the module can be more flexible and allow more control. For example, this module would return +the CPU brand of the Minion: + +.. code-block:: python + + from sysinspect import SysinspectReturn + from syscore import MinionTraits + + def main(*args, **kw) -> str: + r = SysinspectReturn() + r.add_data({ + "cpu": MinionTraits().get("hardware.cpu.brand") + } + ) + + return str(r) + +Hacking +------- + +In order to call own Python module and work on it, the easiest way is to just define an +action in an own model and start calling it locally, using ``sysinspect`` utility: + +.. code-block:: shell + + sysinspect -m -e -s + +Ansible Integration +=================== + +Essentially, the module is ``modules/cfg/bridge.py`` and is called as ``cfg.mgmt``. It can +call only Ansible Modules (not Ansible Action Modules!) and is called quite straightforward: + +.. code-block:: yaml + + actions: + ansible-module: + descr: Call an Ansible module + module: cfg.bridge + bind: + - addresses + + state: + interfaces: + opts: + # Module name + - copy + args: + # Module args, just like in Ansible playbook + src: /etc/networks + dest: /tmp/networks.copy + mode: 0400 + +In this case, an Action which is bound to an Entity ``addresses`` will start bridge module, +which will call an Ansible built-in module ``copy`` with all the required args for it.