Authoring Jobs¶
This section is the practical guide to writing Huginn jobs. It is organized by archetype because each archetype has a distinct shape, and writing one well means following that shape closely.
If you have not yet read Concepts › Job Archetypes, do that first - it covers what the four archetypes are and why each one exists. The rest of this section assumes you understand which archetype your job belongs to.
Pages in this section¶
The remainder of this page covers conventions that apply to every archetype. The per-archetype pages above pick up where this one leaves off and assume you have read it.
Conventions shared across all archetypes¶
Before reading the per-archetype pages, internalize these conventions. They apply universally and are not repeated on every page.
One job per module¶
Every job lives in its own Python module containing exactly one job class. The filename and class name follow the archetype's prefix convention:
| Archetype | Filename prefix | Class prefix |
|---|---|---|
| Static parameter validation | verify_* |
Verify* |
| Volatile parameter validation | verify_*_increasing (or similar comparison-direction suffix) |
Verify*Increasing |
| Change | change_* (and change_*_restore for paired restore jobs) |
Change* / Change*Restore |
| Gate | gate_* (often gate_*_present or gate_*_absent) |
Gate* |
Module structure¶
Every job module follows the same top-to-bottom layout:
- Module docstring (one line).
- Standard library imports.
- Third-party imports (
muninn,huginn). - Local package imports.
- Module-level helper objects (typically
mn = muninn.Muninn(); mn.load_builtin_parsers()). - Module-level message constants (named in
SCREAMING_SNAKE_CASE, with{placeholder}slots). - Module-level tuning constants (private,
_LIKE_THIS). TypedDictdefinitions for the parameters payload.- The job class.
Volatile jobs are an exception - they do not need message constants or TypedDicts because the framework owns the schema. See Volatile Parameter Validation.
Class metadata¶
Every job class declares four narrative class attributes, in this order:
DESCRIPTION- one or two sentences explaining what the job validates or does.SETUP- bulleted list of preconditions assumed by the job.PROCEDURE- bulleted list of steps the job performs. May include inlined Jinja templating to interpolate the parameters payload.PASS_FAIL_CRITERIA- bulleted list of pass and fail conditions.
These attributes are consumed by the reporting system to generate per-test documentation.
Recording results¶
Use context.results.add_result(ResultStatus.X, message) (positional arguments) for outcomes. Use context.results.add_command_execution(...) for every command executed against the testbed. Both are required for the report to render correctly.
Cache control¶
Most jobs read fresh state. Pass use_cache=False when calling context.broker.execute(...) if the read must reflect a state change that just happened in the same job (post-action verification, gate poll, volatile observation).
Command Support¶
Most jobs use check_command_support to skip targets where the relevant show command is not supported. The standard idiom:
async def check_command_support(self, context: Context) -> CommandSupportResult:
applicable = []
not_applicable: dict[str, str] = {}
for device in context.targets:
result = await context.broker.execute(device, self.command)
if is_command_unsupported(result.output):
not_applicable[device.name] = NOT_SUPPORTED_REASON.format(command=self.command)
continue
applicable.append(device)
return CommandSupportResult(applicable=applicable, not_applicable=not_applicable)
Data-level applicability (e.g., a specific parsed field is absent, or per-item extraction yields nothing for a device) is handled in gather_state, not check_command_support. When gather_state determines a device has no meaningful data, it emits a NOT_APPLICABLE result via context.results.add_result() and omits the device from the returned parameters. See Static Parameter Validation § gather_state for the recommended pattern.
See also¶
- Context API Reference - the Context object, Connection Broker API, result recording, and async patterns.