Skip to content

Commit

Permalink
Merge pull request #55 from tinythings/isbm-pydocs
Browse files Browse the repository at this point in the history
Add documentation on Python modules
  • Loading branch information
isbm authored Dec 15, 2024
2 parents 407bef6 + 73d9fb0 commit 32deb5c
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 8 deletions.
36 changes: 28 additions & 8 deletions docs/moddev/overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Modules Development

proto
modstruct
pymod

Overview
--------
Expand All @@ -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``).
118 changes: 118 additions & 0 deletions docs/moddev/pymod.rst
Original file line number Diff line number Diff line change
@@ -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 <path/to/the/model> -e <entity> -s <state>
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.

0 comments on commit 32deb5c

Please sign in to comment.