Module creation¶
Much of cloud-init
’s functionality is provided by modules.
All modules follow a similar layout in order to provide consistent execution
and documentation. Use the example provided here to create a new module.
Your Python module¶
Modules are located in the cloudinit/config/
directory, where the naming
convention for modules is to use cc_<module_name>
(with underscores as the
separators).
The handle function¶
Your module must include a handle
function. The arguments are:
name
: The module name specified in the configuration.cfg
: A configuration object that is the result of the merging of cloud-config configuration with any datasource-provided configuration.cloud
: A cloud object that can be used to access various datasource and paths for the given distro and data provided by the various datasource instance types.args
: An argument list. This is usually empty and is only populated if the module is called independently from the command line or if the module definition in/etc/cloud/cloud.cfg[.d]
has been modified to pass arguments to this module.
Schema definition¶
If your module introduces any new cloud-config keys, you must provide a schema definition in cloud-init-schema.json.
The
meta
variable must exist and be of type MetaSchema.id
: The module ID. In most cases this will be the filename without the.py
extension.distros
: Defines the list of supported distros. It can contain any of the values (not keys) defined in the OSFAMILIES map or[ALL_DISTROS]
if there is no distro restriction.frequency
: Defines how often module runs. It must be one of:PER_ALWAYS
: Runs on every boot.ONCE
: Runs only on first boot.PER_INSTANCE
: Runs once per instance. When exactly this happens is dependent on the datasource, but may be triggered any time there would be a significant change to the instance metadata. An example could be an instance being moved to a different subnet.
activate_by_schema_keys
: Optional list of cloud-config keys that will activate this module. When this list not empty, the config module will be skipped unless one of theactivate_by_schema_keys
are present in merged cloud-config instance-data.
Example module.py file¶
# This file is part of cloud-init. See LICENSE file for license information.
"""Example Module: Shows how to create a module"""
import logging
from cloudinit.cloud import Cloud
from cloudinit.config import Config
from cloudinit.config.schema import MetaSchema
from cloudinit.distros import ALL_DISTROS
from cloudinit.settings import PER_INSTANCE
LOG = logging.getLogger(__name__)
meta: MetaSchema = {
"id": "cc_example",
"distros": [ALL_DISTROS],
"frequency": PER_INSTANCE,
"activate_by_schema_keys": ["example_key, example_other_key"],
} # type: ignore
def handle(
name: str, cfg: Config, cloud: Cloud, args: list
) -> None:
LOG.debug(f"Hi from module {name}")
Module documentation¶
Every module has a folder in the doc/module-docs/
directory, containing
a data.yaml
file, and one or more example*.yaml
files.
The
data.yaml
file contains most of the documentation fields. At a minimum, your module should be provided with this file. Examples are not strictly required, but are helpful to readers of the documentation so it is preferred for at least one example to be included.The
example*.yaml
files are illustrative demonstrations of using the module, but should be self-contained and in correctly-formatted YAML. These will be automatically tested against the defined schema.
Example data.yaml file¶
cc_module_name:
description: >
This module provides some functionality, which you can describe here.
For straightforward text examples, use a greater-than (``>``) symbol
next to ``description: `` to ensure proper rendering in the
documentation. Empty lines will be respected, but line-breaks are
folded together to create proper paragraphs.
If you need to use call-outs or code blocks, use a pipe (``|``) symbol
instead of ``>`` so that reStructuredText formatting (e.g. for
directives, which take varying levels of indentation) is respected.
examples:
- comment: |
Example 1: (optional) description of the expected behavior of the example
file: cc_module_name/example1.yaml
- comment: |
Example 2: (optional) description of a second example.
file: cc_module_name/example2.yaml
name: Module Name
title: Very brief (1 sentence) tag line describing what your module does
Rendering the module docs¶
The module documentation is auto-generated via the
doc/rtd/reference/modules.rst
file.
For your module documentation to be shown in the cloud-init docs, you will need to add an entry to this page. Modules are listed in alphabetical order. The entry should be in the following reStructuredText format:
.. datatemplate:yaml:: ../../module-docs/cc_ansible/data.yaml
:template: modules.tmpl
The template pulls information from both your module.py
file, and from its
corresponding entry in the the module-docs
directory.
Module execution¶
For a module to be run, it must be defined in a module run section in
/etc/cloud/cloud.cfg
or /etc/cloud/cloud.cfg.d
on the launched
instance. The three module sections are
cloud_init_modules, cloud_config_modules, and cloud_final_modules,
corresponding to the Network, Config,
and Final boot stages respectively.
Add your module to cloud.cfg.tmpl under the appropriate module section.
Each module gets run in the order listed, so ensure your module is defined
in the correct location based on dependencies. If your module has no particular
dependencies or is not necessary for a later boot stage, it should be placed
in the cloud_final_modules
section before the final-message
module.
Benefits of including your config module in upstream cloud-init¶
Config modules included in upstream cloud-init benefit from ongoing maintenance, compatibility with the rest of the codebase, and security fixes by the upstream development team.
If this is not possible, one can add custom out-of-tree config modules to cloud-init.