Skip to main content

Module plugin_types

Module plugin_types 

Source
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 host
  • open() - Establish connection with resolved parameters
  • close() - Tear down connection and cleanup resources
  • is_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 task
  • run_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:

§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§

PluginInfo
Information about a loaded plugin, including the plugin itself and its group.

Enums§

PluginEntry
Plugin entry in metadata, either a single path or a named group of paths.
Plugins
Heterogeneous container for supported plugin trait objects.

Traits§

AsyncPluginInventory
Loads or prepares inventory data for the system asynchronously.
Plugin
Base plugin interface implemented by all plugins.
PluginConnection
Manages device connections for plugins that need an explicit session.
PluginInventory
Loads or prepares inventory data for the system.
PluginProcessor
Provides task-result processing hooks.
PluginRunner
Executes tasks against a set of hosts.
PluginTransformFunction
Provides an inventory transform function.

Type Aliases§

GroupOrName
Shared alias for a group name or plugin name key.
PathString
Filesystem path to a plugin or plugin metadata entry.
PluginCreate
Signature for a plugin factory function exported by dynamic libraries.
PluginCreatePlugins
Signature for a plugin factory function exported by dynamic libraries.
PluginName
Display name used to identify a plugin in the registry.
PluginResult
Result of loading a plugin library and its exported plugin instances.
PluginResultPlugins
Result of loading a plugin library and its exported plugin instances.