Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions python/understack-workflows/docs/example_netapp_config.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Example NetApp configuration file showing the new netapp_nic_slot_prefix option

[netapp_nvme]
# Required configuration options
netapp_server_hostname = netapp-cluster.example.com
netapp_login = admin
netapp_password = your-secure-password

# Optional: NIC slot prefix for port naming (defaults to 'e4' if not specified)
# This controls the base port name generation in NetappIPInterfaceConfig
# Examples:
# netapp_nic_slot_prefix = e4 # Results in ports like e4a, e4b (default)
# netapp_nic_slot_prefix = e5 # Results in ports like e5a, e5b
# netapp_nic_slot_prefix = e6 # Results in ports like e6a, e6b
netapp_nic_slot_prefix = e5
148 changes: 148 additions & 0 deletions python/understack-workflows/docs/netapp_architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# NetApp Manager Architecture

## Overview

The NetApp Manager uses a layered architecture with dependency injection, providing maintainability, testability, and separation of concerns.

## Architecture Layers

### 1. NetAppManager (Orchestration Layer)

- **File**: `netapp_manager.py`
- **Purpose**: Orchestrates operations across multiple services
- **Key Features**:
- Maintains all existing public method signatures
- Delegates operations to appropriate service layers
- Handles cross-service coordination (e.g., cleanup operations)
- Manages dependency injection for all services

### 2. Service Layer

- **Files**: `netapp_svm_service.py`, `netapp_volume_service.py`, `netapp_lif_service.py`
- **Purpose**: Implements business logic and naming conventions for specific NetApp resource types
- **Key Features**:
- Encapsulates business rules (e.g., SVM naming: `os-{project_id}`)
- Handles resource-specific operations and validation
- Provides clean interfaces for the orchestration layer
- 100% test coverage with mocked dependencies

### 3. Client Abstraction Layer

- **File**: `netapp_client.py`
- **Purpose**: Provides a thin abstraction over the NetApp ONTAP SDK
- **Key Features**:
- Converts between value objects and SDK objects
- Handles low-level NetApp API interactions
- Implements the NetAppClientInterface for testability
- Manages SDK connection lifecycle

### 4. Infrastructure Components

#### Configuration Management

- **File**: `netapp_config.py`
- **Purpose**: Centralized configuration parsing and validation
- **Features**: Type-safe configuration with validation

#### Error Handling

- **File**: `netapp_error_handler.py`
- **Purpose**: Centralized error handling and logging
- **Features**: Context-aware error translation and structured logging

#### Value Objects

- **File**: `netapp_value_objects.py`
- **Purpose**: Immutable data structures for NetApp operations
- **Features**: Type-safe specifications and results for all operations

#### Custom Exceptions

- **File**: `netapp_exceptions.py`
- **Purpose**: Domain-specific exception hierarchy
- **Features**: Structured error information with context

## Dependency Flow

```text
NetAppManager
├── SvmService ──────┐
├── VolumeService ───┼── NetAppClient ── NetApp SDK
├── LifService ──────┘
├── NetAppConfig
└── ErrorHandler
```

## Key Benefits

### 1. Maintainability

- Clear separation of concerns
- Single responsibility principle
- Dependency injection enables easy component replacement

### 2. Testability

- Each layer can be tested in isolation
- Service layer has 100% test coverage
- Mock-friendly interfaces reduce test complexity

### 3. API Stability

- All existing NetAppManager public methods unchanged
- Same method signatures and return values
- Existing code continues to work without modification

### 4. Extensibility

- New NetApp operations can be added at the appropriate layer
- Business logic changes isolated to service layer
- SDK changes isolated to client layer

## Usage Examples

### Basic Usage (Unchanged)

```python
# Existing code continues to work
manager = NetAppManager("/path/to/config.conf")
svm_name = manager.create_svm("project-123", "aggregate1")
volume_name = manager.create_volume("project-123", "1TB", "aggregate1")
```

### Advanced Usage with Dependency Injection

```python
# For testing or custom configurations
config = NetAppConfig("/custom/config.conf")
error_handler = ErrorHandler()
client = NetAppClient(config, error_handler)
svm_service = SvmService(client, error_handler)

# Use services directly if needed
svm_name = svm_service.create_svm("project-123", "aggregate1")
```

## Testing Strategy

### Unit Tests

- Each service tested with mocked NetAppClient
- Value objects tested for validation and immutability
- Configuration and error handling tested independently

### Integration Tests

- NetAppManager tested with mocked services
- Cross-service coordination tested (e.g., cleanup operations)
- API compatibility verified

## Potential Future Enhancements

The new architecture enables several future improvements:

1. **Async Operations**: Service layer can be enhanced with async/await
2. **Caching**: Client layer can add intelligent caching
3. **Metrics**: Error handler can emit metrics for monitoring
4. **Multi-tenancy**: Service layer can handle multiple NetApp clusters
5. **Configuration Hot-reload**: Config layer can support dynamic updates
1 change: 1 addition & 0 deletions python/understack-workflows/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ bmc-kube-password = "understack_workflows.main.bmc_display_password:main"
sync-network-segment-range = "understack_workflows.main.sync_ucvni_group_range:main"
openstack-oslo-event = "understack_workflows.main.openstack_oslo_event:main"
netapp-create-svm = "understack_workflows.main.netapp_create_svm:main"
netapp-configure-interfaces = "understack_workflows.main.netapp_configure_net:main"

[dependency-groups]
test = [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
{
"data": {
"virtual_machines": [
{
"interfaces": [
{
"name": "N1-lif-A",
"ip_addresses": [
{
"address": "100.127.0.21/29"
}
],
"tagged_vlans": [
{
"vid": 2002
}
]
},
{
"name": "N1-lif-B",
"ip_addresses": [
{
"address": "100.127.128.21/29"
}
],
"tagged_vlans": [
{
"vid": 2002
}
]
},
{
"name": "N2-lif-A",
"ip_addresses": [
{
"address": "100.127.0.22/29"
}
],
"tagged_vlans": [
{
"vid": 2002
}
]
},
{
"name": "N2-lif-B",
"ip_addresses": [
{
"address": "100.127.128.22/29"
}
],
"tagged_vlans": [
{
"vid": 2002
}
]
}
]
}
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"data": {
"virtual_machines": []
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"errors": [
{
"message": "GraphQL syntax error"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"data": {
"virtual_machines": [
{
"interfaces": [
{
"name": "invalid-interface",
"ip_addresses": [
{
"address": "192.168.1.10/24"
},
{
"address": "192.168.1.11/24"
}
],
"tagged_vlans": [
{
"vid": 100
}
]
}
]
}
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"data": {
"virtual_machines": [
{
"interfaces": [
{
"name": "N1-lif-A",
"ip_addresses": [
{
"address": "100.127.0.21/29"
}
],
"tagged_vlans": [
{
"vid": 2002
}
]
},
{
"name": "N1-lif-B",
"ip_addresses": [
{
"address": "100.127.128.21/29"
}
],
"tagged_vlans": [
{
"vid": 2002
}
]
}
]
}
]
}
}
Loading
Loading