Adding an Implementation Under Test (IUT) to PANTHER¤
This guide walks through the process of creating a new Implementation Under Test (IUT) plugin for the PANTHER framework.
Overview¤
IUT plugins represent implementations of protocols that need to be tested within the PANTHER framework. These plugins provide the necessary interface between the PANTHER system and the actual protocol implementation being tested.
Implementation Steps¤
1. Create the Plugin Directory Structure¤
Create the following directory structure for your IUT plugin:
panther/plugins/services/iut/protocol_name/your_iut_name/
├── __init__.py
├── your_iut_name.py
├── config_schema.py
├── Dockerfile
└── README.md
Where:
protocol_name
: The name of the protocol this IUT implements (e.g., quic, http)your_iut_name
: The name of your specific implementation (e.g., picoquic, nginx)
2. Define the Configuration Schema¤
Create a configuration schema in config_schema.py
that defines the parameters your IUT accepts:
from dataclasses import dataclass, field
from typing import Dict, List, Optional
from panther.plugins.services.iut.config_schema import IUTConfig
@dataclass
class YourIUTConfig(IUTConfig):
"""Configuration for YourIUT implementation."""
binary_path: str = "/usr/local/bin/your_binary" # Path to the binary
extra_args: List[str] = field(default_factory=list) # Extra command line arguments
environment_vars: Dict[str, str] = field(default_factory=dict) # Environment variables
config_file_template: Optional[str] = None # Optional config file template
custom_parameter: str = "default_value" # Add your custom parameters here
3. Implement the IUT Class¤
Create your main plugin implementation in your_iut_name.py
:
from panther.plugins.services.iut.iut_interface import IIUT
from panther.core.observer.event_manager import EventManager
from panther.plugins.services.config_schema import ServiceConfig
class YourIUT(IIUT):
"""
YourIUT is a class that manages the YourIUT implementation of the protocol.
"""
def __init__(
self,
service_config: ServiceConfig,
output_dir: str,
service_type: str,
service_sub_type: str,
event_manager: EventManager,
):
super().__init__(service_config, output_dir, service_type, service_sub_type, event_manager)
def setup_service(self):
"""Set up the service for execution."""
self.logger.info(f"Setting up {self.__class__.__name__}")
# Build command based on role (client/server)
if self.service_config.protocol.role == "server":
self.setup_server()
elif self.service_config.protocol.role == "client":
self.setup_client()
else:
self.logger.error(f"Unknown role: {self.service_config.protocol.role}")
return False
return True
def setup_server(self):
"""Set up the server configuration."""
# Configure server-specific settings
self.run_cmd = {
"run_cmd": {
"command": self.service_config.implementation.binary_path,
"args": [
"-p", str(self.service_config.ports[0].split(":")[0]),
# Add other arguments
] + self.service_config.implementation.extra_args,
"command_env": {
# Add environment variables
**self.service_config.implementation.environment_vars
}
},
"pre_run_cmds": [
# Add any commands to run before starting the service
],
"post_run_cmds": [
# Add any commands to run after the service completes
]
}
def setup_client(self):
"""Set up the client configuration."""
# Get target server information
target_server = self.get_target_server()
# Configure client-specific settings
self.run_cmd = {
"run_cmd": {
"command": self.service_config.implementation.binary_path,
"args": [
"-s", target_server,
# Add other arguments
] + self.service_config.implementation.extra_args,
"command_env": {
# Add environment variables
**self.service_config.implementation.environment_vars
}
},
"pre_run_cmds": [
# Add any commands to run before starting the service
],
"post_run_cmds": [
# Add any commands to run after the service completes
]
}
def get_target_server(self):
"""Get the target server address from configuration."""
if not self.service_config.protocol.target:
self.logger.error("No target server specified")
return "localhost"
return self.service_config.protocol.target
4. Create a Dockerfile¤
Create a Dockerfile that builds the environment for your IUT:
# Use an appropriate base image
FROM ubuntu:22.04
# Install dependencies
RUN apt-get update && apt-get install -y \
build-essential \
git \
cmake \
# Add any other dependencies your IUT needs
&& rm -rf /var/lib/apt/lists/*
# Clone and build your implementation
RUN git clone https://github.com/example/your-implementation.git /src && \
cd /src && \
cmake . && \
make && \
make install
# Set working directory
WORKDIR /app
# Set entrypoint (optional)
ENTRYPOINT ["/usr/local/bin/your_binary"]
5. Create the Plugin Documentation¤
Create a comprehensive README.md file following the PANTHER documentation template:
# Your IUT Name
> **Plugin Type**: Service (IUT)
> **Verified Source Location**: `plugins/services/iut/protocol_name/your_iut_name/`
## Purpose and Overview
Describe your IUT's purpose, the protocol it implements, and its key features.
## Requirements and Dependencies
List all dependencies required by this IUT.
## Configuration Options
Document all configuration parameters.
## Usage Examples
Provide examples of how to use your IUT in PANTHER experiments.
## Extension Points
Document how your IUT can be extended.
## Testing and Verification
Explain how to test your IUT.
## Troubleshooting
Document common issues and solutions.
6. Register the Plugin¤
Make sure your plugin can be discovered by PANTHER by updating the __init__.py
file:
from panther.plugins.services.iut.protocol_name.your_iut_name.your_iut_name import YourIUT
__all__ = ["YourIUT"]
Testing Your IUT¤
- Create a test configuration file:
tests:
- name: "Test with Your IUT"
network_environment:
type: "docker_compose"
services:
server:
name: "your_server"
implementation:
name: "your_iut_name"
type: "iut"
binary_path: "/usr/local/bin/your_binary"
extra_args: ["--debug"]
protocol:
name: "protocol_name"
version: "1.0"
role: "server"
ports:
- "8080:8080"
client:
name: "your_client"
implementation:
name: "your_iut_name"
type: "iut"
protocol:
name: "protocol_name"
version: "1.0"
role: "client"
target: "your_server"
- Run PANTHER with your test configuration:
python -m panther -c path/to/your/test/config.yaml
- Debug any issues and refine your implementation.
Best Practices¤
- Protocol Conformance: Ensure your IUT correctly implements the protocol specification
- Proper Configuration: Make configuration options flexible but with sensible defaults
- Logging: Use the logger provided by the base class for consistent logging
- Client/Server Roles: Clearly handle different roles (client/server) in your implementation
- Documentation: Keep your README.md updated with accurate information and usage examples
Example Implementation¤
For reference, you can examine existing IUT plugins:
- Picoquic (QUIC):
plugins/services/iut/quic/picoquic/
- HTTP Implementation:
plugins/services/iut/http/