Modular Role Design Philosophy

This document explains why the Qubinode KVM Host Setup Collection adopts a modular role-based architecture and how this design philosophy benefits users, developers, and the overall project ecosystem.

🎯 Why Modular Design?

The Problem with Monolithic Approaches

Traditional “all-in-one” automation approaches suffer from several limitations:

  1. Tight Coupling: Changes to one feature affect unrelated functionality

  2. Testing Complexity: Difficult to test individual components in isolation

  3. Maintenance Burden: Large, complex roles are hard to understand and modify

  4. Reusability Issues: Can’t use parts of the functionality independently

  5. Collaboration Challenges: Multiple developers working on the same large codebase

The Modular Solution

Our modular approach addresses these challenges by:

  1. Separation of Concerns: Each role has a single, well-defined responsibility

  2. Loose Coupling: Roles interact through well-defined interfaces

  3. Independent Testing: Each role can be tested and validated separately

  4. Selective Usage: Users can choose which components they need

  5. Parallel Development: Teams can work on different roles simultaneously

🏗️ Architectural Principles

1. Single Responsibility Principle

Each role focuses on one specific aspect of KVM host management:

kvmhost_base: Foundation

  • OS detection and validation

  • Base package installation

  • Essential service configuration

  • EPEL repository management

kvmhost_networking: Network Infrastructure

  • Bridge creation and management

  • Network interface configuration

  • Connectivity validation

kvmhost_libvirt: Virtualization Platform

  • Libvirt daemon configuration

  • Virtual network management

  • Hardware feature detection

kvmhost_storage: Storage Management

  • Storage pool creation

  • LVM configuration

  • Performance optimization

kvmhost_cockpit: Management Interface

  • Web interface installation

  • SSL configuration

  • User access management

kvmhost_user_config: User Environment

  • User account management

  • Shell configuration

  • Development tools

2. Dependency Inversion Principle

Roles depend on abstractions, not concrete implementations:

# Instead of hard-coding specific implementations
tasks:
  - name: Configure bridge
    command: brctl addbr br0  # Concrete implementation

# We use abstracted interfaces
tasks:
  - name: Configure bridge
    community.general.nmcli:  # Abstract interface
      conn_name: "{{ bridge_name }}"
      type: bridge

3. Interface Segregation

Each role exposes only the interfaces it needs:

# kvmhost_networking role interface
input_interface:
  required_vars:
    - kvm_host_interface
    - qubinode_bridge_name
  optional_vars:
    - bridge_config
    - validation_enabled

output_interface:
  facts_provided:
    - bridge_created
    - primary_interface_detected
    - network_validation_results

🔄 Role Interaction Patterns

1. Sequential Dependency Pattern

Roles execute in a specific order based on dependencies:

kvmhost_base (foundation)
├── kvmhost_networking (requires: base)
├── kvmhost_user_config (requires: base)
└── kvmhost_libvirt (requires: base, networking)
    ├── kvmhost_storage (requires: base, libvirt)
    └── kvmhost_cockpit (requires: base, libvirt)

2. Configuration Inheritance Pattern

Roles inherit and extend configuration from their dependencies:

# kvmhost_base provides
base_packages: [curl, wget, git]

# kvmhost_libvirt extends
libvirt_packages: "{{ base_packages + ['libvirt-daemon', 'qemu-kvm'] }}"

# kvmhost_storage further extends
storage_packages: "{{ libvirt_packages + ['lvm2', 'parted'] }}"

3. Event-Driven Pattern

Roles communicate through Ansible facts and handlers:

# kvmhost_networking sets facts
- name: Set bridge creation fact
  ansible.builtin.set_fact:
    bridge_created: true
    bridge_name: "{{ qubinode_bridge_name }}"

# kvmhost_libvirt uses these facts
- name: Configure libvirt network
  when: bridge_created | default(false)

🎨 Design Benefits

For End Users

Flexibility

Users can choose which components to deploy:

# Minimal setup - just base and libvirt
roles:
  - kvmhost_base
  - kvmhost_libvirt

# Full setup - all components
roles:
  - kvmhost_setup  # Orchestrates all roles

Customization

Each role can be configured independently:

# Custom networking without storage
- role: kvmhost_networking
  vars:
    qubinode_bridge_name: "custom-br0"
    network_validation_enabled: false

# Skip user configuration
- role: kvmhost_setup
  vars:
    configure_shell: false

Troubleshooting

Issues can be isolated to specific components:

# Test only networking
ansible-playbook playbook.yml --tags networking

# Skip problematic components
ansible-playbook playbook.yml --skip-tags cockpit

For Developers

Focused Development

Developers can focus on specific areas of expertise:

  • Network specialists work on kvmhost_networking

  • Storage experts enhance kvmhost_storage

  • Security teams improve kvmhost_base

Independent Testing

Each role has its own test suite:

# Test individual roles
cd roles/kvmhost_networking
molecule test

# Test role interactions
ansible-playbook test-modular.yml

Parallel Development

Multiple teams can work simultaneously:

  • Team A: Enhancing storage management

  • Team B: Improving network configuration

  • Team C: Adding new user features

For Operations

Selective Deployment

Deploy only needed components:

# Edge deployment - minimal footprint
roles:
  - kvmhost_base
  - kvmhost_libvirt

# Full datacenter deployment
roles:
  - kvmhost_setup  # Everything

Incremental Updates

Update components independently:

# Update only networking components
ansible-playbook playbook.yml --tags networking

# Update storage without affecting network
ansible-playbook playbook.yml --tags storage

🔧 Implementation Strategies

1. Role Interface Design

Each role defines clear interfaces:

# Role input interface (defaults/main.yml)
role_input:
  required_variables:
    - admin_user
    - kvm_host_interface
  optional_variables:
    - custom_config
    - feature_flags

# Role output interface (facts set)
role_output:
  facts_provided:
    - component_configured
    - configuration_status
    - validation_results

2. Configuration Management

Centralized configuration with role-specific overrides:

# Global configuration (group_vars/all.yml)
admin_user: "kvmadmin"
kvm_host_domain: "lab.example.com"

# Role-specific configuration (host_vars/host.yml)
kvmhost_networking_bridge_name: "custom-br0"
kvmhost_storage_pool_size: "100G"

3. Error Handling Strategy

Each role implements consistent error handling:

# Validation with clear error messages
- name: Validate required variables
  ansible.builtin.assert:
    that:
      - admin_user is defined
      - admin_user | length > 0
    fail_msg: "admin_user must be defined and non-empty"

# Graceful degradation
- name: Optional feature configuration
  block:
    - name: Configure advanced feature
      # ... configuration tasks
  rescue:
    - name: Log feature unavailable
      ansible.builtin.debug:
        msg: "Advanced feature not available, continuing with basic setup"

📈 Scalability Considerations

Horizontal Scaling

The modular design supports scaling across multiple hosts:

# Different roles for different host types
- hosts: kvm_compute_nodes
  roles:
    - kvmhost_base
    - kvmhost_libvirt
    - kvmhost_storage

- hosts: kvm_management_nodes
  roles:
    - kvmhost_base
    - kvmhost_cockpit
    - kvmhost_user_config

Vertical Scaling

Roles adapt to different resource levels:

# High-performance configuration
kvmhost_libvirt_performance_mode: high
kvmhost_storage_use_ssd: true
kvmhost_networking_jumbo_frames: true

# Resource-constrained configuration
kvmhost_libvirt_performance_mode: minimal
kvmhost_storage_use_compression: true

🔄 Evolution and Extensibility

Adding New Roles

The modular design makes it easy to add new functionality:

# New role: kvmhost_monitoring
dependencies:
  - kvmhost_base  # Foundation
  - kvmhost_libvirt  # For VM metrics

responsibilities:
  - Monitoring agent installation
  - Metrics collection configuration
  - Alerting setup

Extending Existing Roles

Roles can be extended without breaking existing functionality:

# kvmhost_storage extension for cloud storage
new_features:
  - cloud_storage_integration
  - automated_backup
  - disaster_recovery

backward_compatibility:
  - existing_variables_preserved
  - default_behavior_unchanged
  - migration_path_provided

🎓 Learning from the Design

Design Patterns Applied

  1. Facade Pattern: kvmhost_setup provides a simple interface to complex subsystems

  2. Strategy Pattern: Different implementations for different platforms

  3. Observer Pattern: Roles react to changes in system state

  4. Template Method Pattern: Common task patterns with role-specific implementations

Lessons Learned

  1. Start Simple: Begin with basic functionality, add complexity gradually

  2. Define Interfaces Early: Clear interfaces prevent integration issues

  3. Test Boundaries: Test role interactions as much as individual roles

  4. Document Decisions: ADRs capture the reasoning behind design choices