Creating Custom Wrappers and Aliases#
Learn how to create your own convenience wrappers and aliases for common operations.
Overview#
While HFortix doesn’t include built-in convenience wrappers, you can easily create your own custom wrappers, aliases, and helper functions tailored to your specific needs.
Why Create Custom Wrappers?#
Simplify repetitive tasks - Abstract away complex API calls
Domain-specific logic - Add business logic specific to your organization
Parameter validation - Add custom validation before API calls
Type safety - Create strongly-typed interfaces for your use cases
Reusability - Share common patterns across your team
Simple Aliases#
Create simple aliases for frequently used endpoints:
from hfortix_fortios import FortiOS
class MyFortiGate(FortiOS):
"""Custom FortiGate client with aliases."""
@property
def addresses(self):
"""Quick access to firewall addresses."""
return self.api.cmdb.firewall.address
@property
def policies(self):
"""Quick access to firewall policies."""
return self.api.cmdb.firewall.policy
@property
def interfaces(self):
"""Quick access to system interfaces."""
return self.api.cmdb.system.interface
# Usage
fgt = MyFortiGate(host="192.168.1.99", api_token="your-token")
# Much shorter! (Note: this uses the property shortcuts, not built-in methods)
address = fgt.addresses.post(name="server", subnet="10.0.1.5 255.255.255.255")
policies = fgt.policies.get()
Wrapper Functions#
Create standalone wrapper functions for common operations:
from hfortix_fortios import FortiOS
from typing import List
def create_address_group(
fgt: FortiOS,
name: str,
members: List[str],
comment: str = ""
) -> dict:
"""
Create a firewall address group with simple member list.
Args:
fgt: FortiOS client instance
name: Name of the address group
members: List of member address names
comment: Optional comment
Returns:
API response with created group details
"""
return fgt.api.cmdb.firewall.addrgrp.post(
name=name,
member=[{"name": m} for m in members],
comment=comment
)
# Usage
fgt = FortiOS(host="192.168.1.99", api_token="your-token")
group = create_address_group(
fgt,
name="web-servers",
members=["web1", "web2", "web3"],
comment="Production web servers"
)
Custom Wrapper Class#
Create a full wrapper class with business logic:
from hfortix_fortios import FortiOS
from typing import List, Optional
class FirewallManager:
"""High-level firewall policy management."""
def __init__(self, fgt: FortiOS):
self.fgt = fgt
self.policies = fgt.api.cmdb.firewall.policy
def create_web_policy(
self,
name: str,
source_zones: List[str],
dest_zones: List[str],
allow_http: bool = True,
allow_https: bool = True,
enable_logging: bool = True
) -> dict:
"""
Create a standardized web access policy.
This enforces your organization's standards:
- Always logs connections
- Uses standard service objects
- Includes security profiles
"""
services = []
if allow_http:
services.append({"name": "HTTP"})
if allow_https:
services.append({"name": "HTTPS"})
return self.policies.post(
name=name,
srcintf=[{"name": z} for z in source_zones],
dstintf=[{"name": z} for z in dest_zones],
srcaddr=[{"name": "all"}],
dstaddr=[{"name": "all"}],
service=services,
action="accept",
logtraffic="all" if enable_logging else "disable",
# Add your org's required security profiles
av_profile={"name": "default"},
webfilter_profile={"name": "default"},
ips_sensor={"name": "default"}
)
def bulk_disable_policies(self, policy_ids: List[int]) -> List[dict]:
"""Disable multiple policies at once."""
results = []
for policy_id in policy_ids:
result = self.policies.put(
policyid=policy_id,
status="disable"
)
results.append(result)
return results
def find_policies_by_address(self, address_name: str) -> List[dict]:
"""Find all policies using a specific address."""
all_policies = self.policies.get().results
matching = []
for policy in all_policies:
# Check source addresses
for addr in policy.get('srcaddr', []):
if addr.get('name') == address_name:
matching.append(policy)
break
# Check dest addresses
for addr in policy.get('dstaddr', []):
if addr.get('name') == address_name:
matching.append(policy)
break
return matching
# Usage
fgt = FortiOS(host="192.168.1.99", api_token="your-token")
fw = FirewallManager(fgt)
# Create standardized policy
policy = fw.create_web_policy(
name="Internal-to-DMZ-Web",
source_zones=["internal"],
dest_zones=["dmz"],
allow_http=True,
allow_https=True
)
# Bulk operations
fw.bulk_disable_policies([10, 11, 12, 13])
# Find dependencies
policies = fw.find_policies_by_address("old-server")
Async Wrappers#
Create async wrappers for concurrent operations:
from hfortix_fortios import AsyncFortiOS
from typing import List
import asyncio
class AsyncFirewallManager:
"""Async firewall operations."""
def __init__(self, fgt: AsyncFortiOS):
self.fgt = fgt
async def bulk_create_addresses(
self,
addresses: List[dict]
) -> List[dict]:
"""Create multiple addresses concurrently."""
tasks = [
self.fgt.api.cmdb.firewall.address.post(**addr)
for addr in addresses
]
return await asyncio.gather(*tasks)
async def get_all_objects(self) -> dict:
"""Fetch multiple object types concurrently."""
tasks = {
'addresses': self.fgt.api.cmdb.firewall.address.get(),
'groups': self.fgt.api.cmdb.firewall.addrgrp.get(),
'services': self.fgt.api.cmdb.firewall.service.custom.get(),
'policies': self.fgt.api.cmdb.firewall.policy.get(),
}
results = {}
for key, task in tasks.items():
results[key] = await task
return results
# Usage
async def main():
async with AsyncFortiOS(
host="192.168.1.99",
api_token="your-token"
) as fgt:
fw = AsyncFirewallManager(fgt)
# Create 100 addresses concurrently
addresses = [
{"name": f"host-{i}", "subnet": f"10.0.1.{i} 255.255.255.255"}
for i in range(100)
]
results = await fw.bulk_create_addresses(addresses)
# Fetch all objects at once
all_objects = await fw.get_all_objects()
asyncio.run(main())
Domain-Specific Wrappers#
Create wrappers for your specific infrastructure:
from hfortix_fortios import FortiOS
from dataclasses import dataclass
from typing import List
@dataclass
class Branch:
"""Branch office configuration."""
name: str
subnet: str
vpn_endpoint: str
allowed_services: List[str]
class BranchOfficeManager:
"""Manage branch office configurations."""
def __init__(self, fgt: FortiOS):
self.fgt = fgt
def provision_branch(self, branch: Branch) -> dict:
"""
Complete branch office provisioning.
Creates:
1. Address object for branch subnet
2. Address group for branch
3. Firewall policy for branch access
4. VPN configuration (if needed)
"""
results = {}
# Create address object
results['address'] = self.fgt.api.cmdb.firewall.address.post(
name=f"{branch.name}-subnet",
subnet=branch.subnet,
comment=f"{branch.name} branch office network"
)
# Create address group
results['group'] = self.fgt.api.cmdb.firewall.addrgrp.post(
name=f"{branch.name}-networks",
member=[{"name": f"{branch.name}-subnet"}],
comment=f"{branch.name} branch networks"
)
# Create policy
results['policy'] = self.fgt.api.cmdb.firewall.policy.post(
name=f"{branch.name}-to-HQ",
srcintf=[{"name": "vpn-tunnel"}],
dstintf=[{"name": "internal"}],
srcaddr=[{"name": f"{branch.name}-subnet"}],
dstaddr=[{"name": "HQ-networks"}],
service=[{"name": svc} for svc in branch.allowed_services],
action="accept",
logtraffic="all"
)
return results
# Usage
fgt = FortiOS(host="192.168.1.99", api_token="your-token")
manager = BranchOfficeManager(fgt)
branch = Branch(
name="London",
subnet="10.10.1.0 255.255.255.0",
vpn_endpoint="london-fw.example.com",
allowed_services=["HTTP", "HTTPS", "DNS", "SMTP"]
)
results = manager.provision_branch(branch)
Validation Wrappers#
Add custom validation before API calls:
from hfortix_fortios import FortiOS
from hfortix_core.exceptions import APIError, ResourceNotFoundError
import ipaddress
class ValidatedFirewallManager:
"""Firewall manager with validation."""
def __init__(self, fgt: FortiOS):
self.fgt = fgt
def create_address_with_validation(
self,
name: str,
subnet: str,
comment: str = ""
) -> dict:
"""
Create address with validation.
Validates:
- Name follows naming convention
- Subnet is valid CIDR
- Address doesn't already exist
"""
# Validate name
if not name.replace('-', '').replace('_', '').isalnum():
raise ValueError(f"Invalid name: {name}. Use only alphanumeric, dash, underscore")
# Validate subnet
try:
# Convert FortiOS format to CIDR
ip, mask = subnet.split()
network = ipaddress.IPv4Network(f"{ip}/{mask}", strict=False)
except ValueError as e:
raise ValueError(f"Invalid subnet: {subnet}") from e
# Check if exists
try:
existing = self.fgt.api.cmdb.firewall.address.get(mkey=name)
raise ValueError(f"Address '{name}' already exists")
except ResourceNotFoundError:
pass # Address doesn't exist, we can create it
except APIError as e:
raise # Re-raise other API errors
# Create if validation passes
return self.fgt.api.cmdb.firewall.address.post(
name=name,
subnet=subnet,
comment=comment
)
# Usage
fgt = FortiOS(host="192.168.1.99", api_token="your-token")
manager = ValidatedFirewallManager(fgt)
try:
addr = manager.create_address_with_validation(
name="web-server-01",
subnet="192.168.1.100 255.255.255.255",
comment="Production web server"
)
except ValueError as e:
print(f"Validation error: {e}")
Best Practices#
Start simple - Begin with basic aliases and grow complexity as needed
Type hints - Use Python type hints for better IDE support
Documentation - Document your wrappers with docstrings
Error handling - Wrap API calls in try/except blocks
Reusability - Design wrappers to be reused across projects
Testing - Test your wrappers thoroughly
Keep it DRY - Don’t repeat yourself - create wrappers for repeated patterns
Example: Complete Custom Module#
Here’s a complete example you can use as a template:
# my_fortios_helpers.py
"""
Custom FortiOS helper functions and wrappers.
"""
from hfortix_fortios import FortiOS
from typing import List, Optional, Dict, Any
from hfortix_core.exceptions import HTTPError
class FortiOSHelper:
"""Collection of helper methods for common FortiOS operations."""
def __init__(self, host: str, api_token: Optional[str] = None, **kwargs):
"""Initialize helper with FortiOS connection."""
self.fgt = FortiOS(host=host, api_token=api_token, **kwargs)
# Shortcuts
@property
def addr(self):
"""Shortcut to firewall addresses."""
return self.fgt.api.cmdb.firewall.address
@property
def pol(self):
"""Shortcut to firewall policies."""
return self.fgt.api.cmdb.firewall.policy
# Helper methods
def create_simple_policy(
self,
name: str,
src_zone: str,
dst_zone: str,
services: List[str],
**kwargs
) -> Dict[str, Any]:
"""Create a simple any-to-any policy with specified services."""
return self.pol.post(
name=name,
srcintf=[{"name": src_zone}],
dstintf=[{"name": dst_zone}],
srcaddr=[{"name": "all"}],
dstaddr=[{"name": "all"}],
service=[{"name": s} for s in services],
action="accept",
**kwargs
)
def __enter__(self):
"""Context manager support."""
return self
def __exit__(self, *args):
"""Cleanup on context exit."""
self.fgt.close()
# Usage
with FortiOSHelper(host="192.168.1.99", api_token="your-token") as fgt:
# Use shortcuts
addr = fgt.addr.post(name="test", subnet="10.0.0.1 255.255.255.255")
# Use helpers
policy = fgt.create_simple_policy(
name="Allow-Web",
src_zone="internal",
dst_zone="wan",
services=["HTTP", "HTTPS"]
)