Skip to main content

Module connection_factory

Module connection_factory 

Source
Expand description

Connection factory implementation for plugin-based connections.

This module provides the infrastructure to integrate plugin-based connections into the core inventory system’s connection management. It bridges the gap between the PluginConnection trait used by plugins and the Connection trait expected by the inventory system.

§Overview

The module consists of two main components:

  1. [PluginConnectionAdapter] - An adapter that wraps PluginConnection implementations and provides the Connection interface. It tracks connection lifecycle state and delegates operations to the underlying plugin.

  2. build_connection_factory - A factory function that creates a ConnectionFactory closure. This factory looks up plugins by connection type and creates appropriate connection instances wrapped in adapters.

§Architecture

┌─────────────────────────────────────────────────────────────┐
│                    Connection Manager                       │
│                  (genja_core::inventory)                    │
└─────────────────────────┬───────────────────────────────────┘
                          │
                          │ Uses ConnectionFactory
                          ▼
┌─────────────────────────────────────────────────────────────┐
│              build_connection_factory()                     │
│         Returns: Arc<ConnectionFactory>                     │
└─────────────────────────┬───────────────────────────────────┘
                          │
                          │ Queries for plugins
                          ▼
┌─────────────────────────────────────────────────────────────┐
│                    PluginManager                            │
│              (Registered Connection Plugins)                │
└─────────────────────────┬───────────────────────────────────┘
                          │
                          │ Returns plugin instance
                          ▼
┌─────────────────────────────────────────────────────────────┐
│              PluginConnectionAdapter                        │
│         Wraps: Box<dyn PluginConnection>                    │
│         Implements: Connection trait                        │
└─────────────────────────┬───────────────────────────────────┘
                          │
                          │ Delegates to
                          ▼
┌─────────────────────────────────────────────────────────────┐
│              Plugin Implementation                          │
│         (e.g., SSH, Telnet, NETCONF)                        │
│         Implements: PluginConnection trait                  │
└─────────────────────────────────────────────────────────────┘

§Usage

§Basic Setup

use genja_plugin_manager::{PluginManager, connection_factory::build_connection_factory};
use genja_core::inventory::{ConnectionManager, ConnectionKey};
use std::sync::Arc;

// 1. Create and configure plugin manager
let mut plugin_manager = PluginManager::new();
// Register connection plugins...

// 2. Build connection factory
let factory = build_connection_factory(Arc::new(plugin_manager));

// 3. Set factory in connection manager
let connection_manager = ConnectionManager::default();
connection_manager.set_connection_factory(factory);

// 4. Use connection manager to create connections
let key = ConnectionKey::new("router1", "ssh");
// let connection = connection_manager.get_or_create(key);

§Plugin Integration

Connection plugins must implement the PluginConnection trait:

use async_trait::async_trait;
use genja_plugin_manager::plugin_types::{Plugin, PluginConnection};
use genja_core::inventory::{ConnectionKey, ResolvedConnectionParams};

struct MyConnectionPlugin {
    key: ConnectionKey,
}

impl Plugin for MyConnectionPlugin {
    fn name(&self) -> String {
        "my_connection".to_string()
    }
}

#[async_trait]
impl PluginConnection for MyConnectionPlugin {
    fn create(&self, key: &ConnectionKey) -> Box<dyn PluginConnection> {
        Box::new(MyConnectionPlugin { key: key.clone() })
    }

    async fn open(&mut self, params: &ResolvedConnectionParams) -> Result<(), String> {
        // Establish connection
        let _ = params;
        Ok(())
    }

    fn close(&mut self) -> ConnectionKey {
        // Clean up connection
        self.key.clone()
    }

    fn is_alive(&self) -> bool {
        // Check connection status
        true
    }
}

§Connection Lifecycle

The adapter manages the connection lifecycle through the following states:

  1. Created - Adapter is instantiated with alive = false
  2. Opening - open() is called with connection parameters
  3. Open - Connection established successfully, alive = true
  4. Closing - close() is called to tear down connection
  5. Closed - Connection terminated, alive = false

§State Transitions

┌─────────┐
│ Created │ (alive = false)
└────┬────┘
     │
     │ open() called
     ▼
┌─────────┐
│ Opening │
└────┬────┘
     │
     ├─ Success ──► ┌──────┐
     │              │ Open │ (alive = true)
     │              └───┬──┘
     │                  │
     │                  │ close() called
     │                  ▼
     │              ┌────────┐
     └─ Failure ──► │ Closed │ (alive = false)
                    └────────┘

§Thread Safety

All components in this module are designed for concurrent use:

  • The connection factory is wrapped in Arc and can be shared across threads
  • Each connection adapter is wrapped in Arc<Mutex<_>> for safe mutation
  • The PluginManager reference is shared via Arc in the factory closure

§Error Handling

The factory returns Option<Arc<Mutex<dyn Connection>>>:

  • Some(connection) - Plugin found and connection created successfully
  • None - Plugin not found or not a connection plugin

Connection operations return Result<(), String>:

  • Ok(()) - Operation succeeded
  • Err(message) - Operation failed with error description

§Performance Considerations

  • Connection Pooling: The ConnectionManager handles connection reuse
  • Lazy Creation: Connections are created only when needed
  • Plugin Lookup: Plugin queries are O(1) hash map lookups
  • Lock Contention: Each connection has its own mutex to minimize contention

§Examples

§Complete Integration Example

use genja_plugin_manager::{PluginManager, connection_factory::build_connection_factory};
use genja_core::inventory::{
    ConnectionManager, ConnectionKey, ResolvedConnectionParams,
    BaseBuilderHost, Host, Hosts, Inventory,
};
use std::sync::Arc;
use tokio::runtime::Builder;

// Set up plugin manager with connection plugins
let mut plugin_manager = PluginManager::new();
// plugin_manager.load_plugins_from_directory("plugins")?;

// Create connection factory
let factory = build_connection_factory(Arc::new(plugin_manager));

// Build inventory with hosts
let mut hosts = Hosts::new();
hosts.add_host("router1", Host::builder()
    .hostname("10.0.0.1")
    .port(22)
    .username("admin")
    .platform("cisco_ios")
    .build());

let inventory = Inventory::builder()
    .hosts(hosts)
    .build();

// Set up connection manager
let connection_manager = inventory.connections();
connection_manager.set_connection_factory(factory);

// Create and use connection
let key = ConnectionKey::new("router1", "ssh");

// Open connection
let params = ResolvedConnectionParams {
    hostname: "10.0.0.1".to_string(),
    port: Some(22),
    username: Some("admin".to_string()),
    password: Some("secret".to_string()),
    platform: Some("cisco_ios".to_string()),
    extras: None,
};
let runtime = Builder::new_current_thread().enable_all().build()?;
let connection = runtime
    .block_on(async { connection_manager.open_connection(&key, &params).await })?
    .expect("connection plugin not found");

let conn = runtime.block_on(connection.lock());

// Use connection...
assert!(conn.is_alive());

// Close connection
drop(conn);
connection_manager.close_connection(&key);

§See Also

Functions§

build_connection_factory
Builds a connection factory that creates connections from registered plugins.