Expand description
Plugin type system and trait definitions for the plugin manager.
This module defines the core plugin architecture used throughout the Genja plugin system.
It provides trait definitions for different plugin types, type aliases for common patterns,
and the Plugins enum for working with heterogeneous plugin collections.
§Overview
The plugin system is built around a hierarchy of traits that define different plugin capabilities:
┌───────────────────────────────────────────────────────────────┐
│ Plugin (Base) │
│ - name() -> String │
│ - group() -> String │
└───────────────────────────┬───────────────────────────────────┘
│
┌─────────────────┼─────────────────┬─────────────────┬─────────────────┬─────────────────┐
│ │ │ │ │ │
▼ ▼ ▼ ▼ ▼ ▼
┌─────────────────┐┌─────────────────┐┌─────────────────┐┌─────────────────┐┌─────────────────┐┌─────────────────┐
│PluginConnection ││PluginInventory ││AsyncPluginInv. ││ PluginRunner ││PluginTransform ││PluginProcessor │
│ ││ ││ ││ ││ Function ││ │
│ - create() ││ - load() ││ - load_async() ││ - run_task() ││ - transform_ ││ - processor() │
│ - open() ││ ││ ││ - run_tasks() ││ function() ││ │
│ - close() ││ ││ ││ ││ ││ │
│ - is_alive() ││ ││ ││ ││ ││ │
└─────────────────┘└─────────────────┘└─────────────────┘└─────────────────┘└─────────────────┘└─────────────────┘§Plugin Types
§Base Plugin Trait
All plugins must implement the Plugin trait, which provides:
- A unique name for identification
- A group classification for organizational purposes
§Specialized Plugin Traits
§PluginConnection
Manages device connections with lifecycle hooks for establishing and tearing down sessions. Used for protocols like SSH, Telnet, NETCONF, etc.
Key Methods:
create()- Create new connection instances per hostopen()- Establish connection with resolved parametersclose()- Tear down connection and cleanup resourcesis_alive()- Check connection health status
§PluginInventory
Loads and prepares inventory data from various sources. Overrides default inventory loading behavior.
Key Methods:
load()- Load inventory from source (files, APIs, databases, etc.)
§AsyncPluginInventory
Loads and prepares inventory data asynchronously for remote sources such as HTTP APIs, databases, or service-discovery systems.
Key Methods:
load_async()- Load inventory from an async source
§PluginRunner
Executes tasks against sets of hosts. Provides different execution strategies (sequential, parallel, etc.).
Key Methods:
run_task()- Execute a single taskrun_tasks()- Execute an ordered list of root task trees
§PluginTransformFunction
Provides inventory transformation functions for normalizing or modifying inventory data during loading.
Key Methods:
transform_function()- Returns the transform function implementation
§PluginProcessor
Provides task-result lifecycle hooks. Processor plugins are registered by
name, and tasks opt into them by listing processor names on the task or with
#[genja_task(processors = ["name"])] when using the task authoring macro.
Key Methods:
processor()- Returns the task processor implementation
§Type Aliases
The module provides several type aliases for common patterns:
PathString- Filesystem path to a plugin libraryGroupOrName- Plugin name or group identifierPluginName- Display name for plugin identificationPluginResult- Result type for plugin loading operationsPluginCreate- Factory function signature for plugin creation
§The Plugins Enum
The Plugins enum provides a heterogeneous container for different plugin types,
allowing them to be stored in a single collection:
use genja_plugin_manager::plugin_types::Plugins;
// Store different plugin types in a single vector
let plugins: Vec<Plugins> = vec![
// Plugins::Connection(Box::new(ssh_plugin)),
// Plugins::Inventory(Box::new(file_plugin)),
// Plugins::AsyncInventory(Box::new(remote_inventory_plugin)),
// Plugins::Processor(Box::new(audit_processor_plugin)),
// Plugins::Runner(Box::new(threaded_runner)),
];§Plugin Metadata
§PluginEntry
The PluginEntry enum represents plugin configuration in metadata:
# Individual plugin
[package.metadata.plugins]
ssh_plugin = "/path/to/libssh_plugin.so"
# Grouped plugins
[package.metadata.plugins.connection]
ssh = "/path/to/libssh.so"
telnet = "/path/to/libtelnet.so"
# Grouped by plugin type
[package.metadata.plugins.processor]
audit = "/path/to/libaudit_processor.so"§PluginInfo
The PluginInfo struct combines a plugin instance with its optional group:
use genja_plugin_manager::plugin_types::PluginInfo;
// let info = PluginInfo {
// plugin: Box::new(my_plugin),
// group: Some("network".to_string()),
// };§Usage Examples
§Implementing a Connection Plugin
use async_trait::async_trait;
use genja_plugin_manager::plugin_types::{Plugin, PluginConnection};
use genja_core::inventory::{ConnectionKey, ResolvedConnectionParams};
#[derive(Debug)]
struct SshPlugin {
key: ConnectionKey,
connected: bool,
}
impl Plugin for SshPlugin {
fn name(&self) -> String {
"ssh".to_string()
}
}
#[async_trait]
impl PluginConnection for SshPlugin {
fn create(&self, key: &ConnectionKey) -> Box<dyn PluginConnection> {
Box::new(SshPlugin {
key: key.clone(),
connected: false,
})
}
async fn open(&mut self, params: &ResolvedConnectionParams) -> Result<(), String> {
// Establish SSH connection
let _ = params;
self.connected = true;
Ok(())
}
fn close(&mut self) -> ConnectionKey {
// Clean up SSH connection
self.connected = false;
self.key.clone()
}
fn is_alive(&self) -> bool {
self.connected
}
}§Implementing an Inventory Plugin
use genja_plugin_manager::plugin_types::{Plugin, PluginInventory};
use genja_plugin_manager::PluginManager;
use genja_core::{Settings, InventoryLoadError};
use genja_core::inventory::Inventory;
#[derive(Debug)]
struct DatabaseInventoryPlugin;
impl Plugin for DatabaseInventoryPlugin {
fn name(&self) -> String {
"database_inventory".to_string()
}
}
impl PluginInventory for DatabaseInventoryPlugin {
fn load(
&self,
settings: &Settings,
plugins: &PluginManager,
) -> Result<Inventory, InventoryLoadError> {
// Load inventory from database
// let inventory = fetch_from_database(settings)?;
// Ok(inventory)
unimplemented!()
}
}§Implementing an Async Inventory Plugin
use async_trait::async_trait;
use genja_plugin_manager::plugin_types::{AsyncPluginInventory, Plugin};
use genja_plugin_manager::PluginManager;
use genja_core::{InventoryLoadError, Settings};
use genja_core::inventory::Inventory;
#[derive(Debug)]
struct RemoteInventoryPlugin;
impl Plugin for RemoteInventoryPlugin {
fn name(&self) -> String {
"remote_inventory".to_string()
}
}
#[async_trait]
impl AsyncPluginInventory for RemoteInventoryPlugin {
async fn load_async(
&self,
settings: &Settings,
plugins: &PluginManager,
) -> Result<Inventory, InventoryLoadError> {
let _ = (settings, plugins);
Ok(Inventory::builder().build())
}
}§Implementing a Runner Plugin
use async_trait::async_trait;
use genja_plugin_manager::plugin_types::{Plugin, PluginRunner};
use genja_core::inventory::Hosts;
use genja_core::settings::RunnerConfig;
use genja_core::task::{TaskDefinition, TaskResults};
#[derive(Debug)]
struct ExampleSequentialRunner;
impl Plugin for ExampleSequentialRunner {
fn name(&self) -> String {
// This is a custom plugin example. The built-in Genja runner name is `serial`.
"example_sequential".to_string()
}
}
#[async_trait]
impl PluginRunner for ExampleSequentialRunner {
async fn run_task(
&self,
task: &TaskDefinition,
hosts: &Hosts,
connection_resolver: Option<std::sync::Arc<dyn genja_core::task::TaskConnectionResolver>>,
runner_config: &RunnerConfig,
max_depth: usize,
) -> Result<TaskResults, genja_core::GenjaError> {
// Execute task sequentially on each host
let _ = (task, hosts, connection_resolver, runner_config, max_depth);
Ok(TaskResults::new("example_sequential"))
}
// `run_tasks(...)` has a default implementation that preserves task order
// and delegates each root task tree to `run_task(...)`. Override it only when
// the runner needs custom batching behavior.
}§Implementing a Processor Plugin
use genja_core::task::{
HostTaskResult, TaskProcessor, TaskProcessorContext, TaskResults,
};
use genja_plugin_manager::plugin_types::{Plugin, PluginProcessor};
use std::sync::Arc;
#[derive(Debug)]
struct AuditProcessorPlugin;
impl Plugin for AuditProcessorPlugin {
fn name(&self) -> String {
"audit".to_string()
}
}
impl PluginProcessor for AuditProcessorPlugin {
fn processor(&self) -> Arc<dyn TaskProcessor> {
Arc::new(AuditProcessor)
}
}
struct AuditProcessor;
impl TaskProcessor for AuditProcessor {
fn on_task_finish(
&self,
context: &TaskProcessorContext,
results: &mut TaskResults,
) -> Result<(), genja_core::GenjaError> {
let _ = (context, results);
Ok(())
}
fn on_instance_finish(
&self,
context: &TaskProcessorContext,
result: &mut HostTaskResult,
) -> Result<(), genja_core::GenjaError> {
let _ = (context, result);
Ok(())
}
}§Implementing a Transform Function Plugin
use genja_plugin_manager::plugin_types::{Plugin, PluginTransformFunction};
use genja_core::inventory::{TransformFunction, Host, BaseBuilderHost};
#[derive(Debug)]
struct NormalizeHostnamePlugin;
impl Plugin for NormalizeHostnamePlugin {
fn name(&self) -> String {
"normalize_hostname".to_string()
}
}
impl PluginTransformFunction for NormalizeHostnamePlugin {
fn transform_function(&self) -> TransformFunction {
TransformFunction::new(|host: &Host, _options| {
// Normalize hostname to lowercase
if let Some(hostname) = host.hostname() {
host.to_builder().hostname(hostname.to_lowercase()).build()
} else {
host.clone()
}
})
}
}§Working with the Plugins Enum
use genja_plugin_manager::plugin_types::Plugins;
fn process_plugin(plugin: &Plugins) {
match plugin {
Plugins::Connection(conn) => {
println!("Connection plugin: {}", conn.name());
}
Plugins::Inventory(inv) => {
println!("Inventory plugin: {}", inv.name());
}
Plugins::AsyncInventory(inv) => {
println!("Async inventory plugin: {}", inv.name());
}
Plugins::Processor(processor) => {
println!("Processor plugin: {}", processor.name());
}
Plugins::Runner(runner) => {
println!("Runner plugin: {}", runner.name());
}
Plugins::TransformFunction(tf) => {
println!("Transform function plugin: {}", tf.name());
}
}
}§Plugin Factory Functions
Plugins are created through factory functions exported from dynamic libraries:
use genja_plugin_manager::plugin_types::Plugins;
#[unsafe(no_mangle)]
pub fn create_plugins() -> Vec<Plugins> {
vec![
// Plugins::Connection(Box::new(SshPlugin::new())),
// Plugins::Processor(Box::new(AuditProcessorPlugin)),
// Plugins::Runner(Box::new(SequentialRunner)),
]
}Structs§
- Plugin
Info - Information about a loaded plugin, including the plugin itself and its group.
Enums§
- Plugin
Entry - Plugin entry in metadata, either a single path or a named group of paths.
- Plugins
- Heterogeneous container for supported plugin trait objects.
Traits§
- Async
Plugin Inventory - Loads or prepares inventory data for the system asynchronously.
- Plugin
- Base plugin interface implemented by all plugins.
- Plugin
Connection - Manages device connections for plugins that need an explicit session.
- Plugin
Inventory - Loads or prepares inventory data for the system.
- Plugin
Processor - Provides task-result processing hooks.
- Plugin
Runner - Executes tasks against a set of hosts.
- Plugin
Transform Function - Provides an inventory transform function.
Type Aliases§
- Group
OrName - Shared alias for a group name or plugin name key.
- Path
String - Filesystem path to a plugin or plugin metadata entry.
- Plugin
Create - Signature for a plugin factory function exported by dynamic libraries.
- Plugin
Create Plugins - Signature for a plugin factory function exported by dynamic libraries.
- Plugin
Name - Display name used to identify a plugin in the registry.
- Plugin
Result - Result of loading a plugin library and its exported plugin instances.
- Plugin
Result Plugins - Result of loading a plugin library and its exported plugin instances.