Core Modules
HTTP Client Framework
The HTTP client framework provides both synchronous and asynchronous HTTP clients with built-in retry logic, circuit breakers, connection pooling, and statistics.
IHTTPClient (Protocol Interface)
- class hfortix_core.http.IHTTPClient(*args, **kwargs)[source]
Bases:
ProtocolProtocol defining the interface for HTTP clients used by FortiOS endpoints.
This protocol allows any class implementing these methods to be used as an HTTP client, enabling: - Custom HTTP client implementations - Easier testing with mock/fake clients - Support for both sync and async clients - Extension by library users
Method signatures support both synchronous (returning dict) and asynchronous (returning Coroutine) implementations. The return type is Union to accommodate both modes.
Implementations: - HTTPClient: Synchronous implementation using httpx.Client - AsyncHTTPClient: Asynchronous implementation using httpx.AsyncClient
Note
All methods should handle vdom=False to skip VDOM parameter in requests. The raw_json parameter controls whether full API response is returned (True) or just the results section (False, default).
- get(api_type, path, params=None, vdom=None, raw_json=False, unwrap_single=False, action=None)[source]
Perform GET request to retrieve resource(s) from the API.
- Parameters:
api_type (
str) – API category (e.g., ‘cmdb’, ‘monitor’, ‘log’, ‘service’)path (
str) – Endpoint path (e.g., ‘firewall/address’,'firewall/address/web-server')
params (
Optional[dict[str,Any]]) – Optional query parameters (filters, pagination, etc.)vdom (
Union[str,bool,None]) – Virtual domain name, or False to skip VDOM parameterraw_json (
bool) – If True, return full API response with metadata; ifFalse
results (return only)
unwrap_single (
bool) – If True and result is single-item list, return just the itemaction (
Optional[str]) – Special action parameter (e.g., ‘schema’, ‘default’)
- Returns:
API response (sync mode) or Coroutine[dict] (async mode)
- Return type:
- Example (Sync):
result = client.get(“cmdb”, “firewall/address/web-server”)
- Example (Async):
result = await client.get(“cmdb”, “firewall/address/web-server”)
- post(api_type, path, data, params=None, vdom=None, raw_json=False)[source]
Perform POST request to create new resource(s) in the API.
- Parameters:
api_type (
str) – API category (e.g., ‘cmdb’, ‘monitor’, ‘log’, ‘service’)path (
str) – Endpoint path (e.g., ‘firewall/address’)params (
Optional[dict[str,Any]]) – Optional query parametersvdom (
Union[str,bool,None]) – Virtual domain name, or False to skip VDOM parameterraw_json (
bool) – If True, return full API response with metadata; ifFalse
results (return only)
- Returns:
API response (sync mode) or Coroutine[dict] (async mode)
- Return type:
- Example (Sync):
result = client.post(“cmdb”, “firewall/address”, data={“name”: “test”, “subnet”: “10.0.0.1/32”})
- Example (Async):
result = await client.post(“cmdb”, “firewall/address”, data={…})
- put(api_type, path, data, params=None, vdom=None, raw_json=False)[source]
Perform PUT request to update existing resource in the API.
- Parameters:
api_type (
str) – API category (e.g., ‘cmdb’, ‘monitor’, ‘log’, ‘service’)path (
str) – Endpoint path with identifier (e.g.,'firewall/address/web-server')
params (
Optional[dict[str,Any]]) – Optional query parametersvdom (
Union[str,bool,None]) – Virtual domain name, or False to skip VDOM parameterraw_json (
bool) – If True, return full API response with metadata; ifFalse
results (return only)
- Returns:
API response (sync mode) or Coroutine[dict] (async mode)
- Return type:
- Example (Sync):
result = client.put(“cmdb”, “firewall/address/web-server”, data={“subnet”: “10.0.0.2/32”})
- Example (Async):
result = await client.put(“cmdb”, “firewall/address/web-server”, data={…})
- delete(api_type, path, params=None, vdom=None, raw_json=False)[source]
Perform DELETE request to remove resource from the API.
- Parameters:
api_type (
str) – API category (e.g., ‘cmdb’, ‘monitor’, ‘log’, ‘service’)path (
str) – Endpoint path with identifier (e.g.,'firewall/address/web-server')
params (
Optional[dict[str,Any]]) – Optional query parametersvdom (
Union[str,bool,None]) – Virtual domain name, or False to skip VDOM parameterraw_json (
bool) – If True, return full API response with metadata; ifFalse
results (return only)
- Returns:
API response (sync mode) or Coroutine[dict] (async mode)
- Return type:
- Example (Sync):
result = client.delete(“cmdb”, “firewall/address/web-server”)
- Example (Async):
result = await client.delete(“cmdb”, “firewall/address/web-server”)
- close()[source]
Close the HTTP client and release resources.
- Return type:
- Returns:
None (sync clients) or Coroutine[None] (async clients)
Optional method - not required for basic protocol compliance. Custom clients may implement this for resource cleanup.
- Example (Sync):
client.close()
- Example (Async):
await client.close()
- get_connection_stats()[source]
Get connection statistics.
Optional method - not required for basic protocol compliance. Returns statistics about HTTP connections if supported.
- get_operations()[source]
Get audit log of all API operations.
Optional method - not required for basic protocol compliance. Only available when operation tracking is enabled.
- get_write_operations()[source]
Get audit log of write operations (POST/PUT/DELETE).
Optional method - not required for basic protocol compliance. Only available when operation tracking is enabled.
- get_retry_stats()[source]
Get retry statistics and metrics.
Optional method - not required for basic protocol compliance. Only available when adaptive retry is enabled.
- get_circuit_breaker_state()[source]
Get current circuit breaker state and metrics.
Optional method - not required for basic protocol compliance. Only available when circuit breaker is enabled.
- get_health_metrics()[source]
Get health metrics and performance indicators.
Optional method - not required for basic protocol compliance. Only available when adaptive retry is enabled.
BaseHTTPClient (Base Client)
- class hfortix_core.http.BaseHTTPClient(url, verify=True, vdom=None, max_retries=3, connect_timeout=10.0, read_timeout=300.0, circuit_breaker_threshold=5, circuit_breaker_timeout=60.0, max_connections=100, max_keepalive_connections=20, adaptive_retry=False, retry_strategy='exponential', retry_jitter=False)[source]
Bases:
objectBase class for HTTP clients with shared logic.
Provides: - Parameter validation - URL building - Retry statistics - Circuit breaker state management - Endpoint timeout configuration - Path normalization and encoding - Data sanitization
- Parameters:
- __init__(url, verify=True, vdom=None, max_retries=3, connect_timeout=10.0, read_timeout=300.0, circuit_breaker_threshold=5, circuit_breaker_timeout=60.0, max_connections=100, max_keepalive_connections=20, adaptive_retry=False, retry_strategy='exponential', retry_jitter=False)[source]
Initialize base HTTP client with shared configuration
- Parameters:
adaptive_retry (
bool) – Enable adaptive retry with backpressure detection(default – False) When enabled, monitors response times and adjusts retry delays based on FortiGate health signals.
retry_strategy (
str) – Retry backoff strategy - ‘exponential’ (default) or ‘linear’. Exponential: 1s, 2s, 4s, 8s, 16s, 30s. Linear: 1s, 2s, 3s, 4s, 5s.retry_jitter (
bool) – Add random jitter (0-25% of delay) to retry delays to prevent thundering herd problem when multiple clients retry simultaneously (default: False).url (str)
verify (bool)
vdom (str | None)
max_retries (int)
connect_timeout (float)
read_timeout (float)
circuit_breaker_threshold (int)
circuit_breaker_timeout (float)
max_connections (int)
max_keepalive_connections (int)
- Return type:
None
HTTPClient (Synchronous FortiOS)
- class hfortix_core.http.HTTPClient(url, verify=True, token=None, username=None, password=None, vdom=None, max_retries=3, connect_timeout=10.0, read_timeout=300.0, user_agent=None, circuit_breaker_threshold=10, circuit_breaker_timeout=30.0, circuit_breaker_auto_retry=False, circuit_breaker_max_retries=3, circuit_breaker_retry_delay=5.0, max_connections=100, max_keepalive_connections=20, session_idle_timeout=300, read_only=False, track_operations=False, adaptive_retry=False, retry_strategy='exponential', retry_jitter=False, audit_handler=None, audit_callback=None, user_context=None)[source]
Bases:
BaseHTTPClientInternal HTTP client for FortiOS API requests (Sync Implementation)
Implements the IHTTPClient protocol for synchronous HTTP operations.
Handles all HTTP communication with FortiGate devices including: - Session management - Authentication headers - SSL verification - Request/response handling - Error handling - Automatic retry with exponential backoff - Context manager support (use with ‘with’ statement)
- Query Parameter Encoding:
The requests library automatically handles query parameter encoding: - Lists: Encoded as repeated parameters (e.g., [‘a’, ‘b’] → ?key=a&key=b) - Booleans: Converted to lowercase strings (‘true’/’false’) - None values: Should be filtered out before passing to params - Special characters: URL-encoded automatically
- Path Encoding:
Paths are URL-encoded with / and % as safe characters to prevent double-encoding of already-encoded components.
- Protocol Implementation:
This class implements the IHTTPClient protocol, allowing it to be used interchangeably with other HTTP client implementations (e.g., AsyncHTTPClient, custom user-provided clients).
This class is internal and not exposed to users directly, but users can provide their own IHTTPClient implementations to FortiOS.__init__().
- Parameters:
url (str)
verify (bool)
token (Optional[str])
username (Optional[str])
password (Optional[str])
vdom (Optional[str])
max_retries (int)
connect_timeout (float)
read_timeout (float)
user_agent (Optional[str])
circuit_breaker_threshold (int)
circuit_breaker_timeout (float)
circuit_breaker_auto_retry (bool)
circuit_breaker_max_retries (int)
circuit_breaker_retry_delay (float)
max_connections (int)
max_keepalive_connections (int)
read_only (bool)
track_operations (bool)
adaptive_retry (bool)
retry_strategy (str)
retry_jitter (bool)
audit_handler (Optional[Any])
audit_callback (Optional[Any])
- __init__(url, verify=True, token=None, username=None, password=None, vdom=None, max_retries=3, connect_timeout=10.0, read_timeout=300.0, user_agent=None, circuit_breaker_threshold=10, circuit_breaker_timeout=30.0, circuit_breaker_auto_retry=False, circuit_breaker_max_retries=3, circuit_breaker_retry_delay=5.0, max_connections=100, max_keepalive_connections=20, session_idle_timeout=300, read_only=False, track_operations=False, adaptive_retry=False, retry_strategy='exponential', retry_jitter=False, audit_handler=None, audit_callback=None, user_context=None)[source]
Initialize HTTP client
- Parameters:
url (
str) – Base URL for API (e.g., “https://192.0.2.10”)verify (
bool) – Verify SSL certificatestoken (
Optional[str]) – API authentication token (if using token auth)username (
Optional[str]) – Username for authentication (if using username/passwordauth)
password (
Optional[str]) – Password for authentication (if using username/passwordauth)
max_retries (
int) – Maximum number of retry attempts on transient failures(default (all API calls) –
connect_timeout (
float) – Timeout for establishing connection in seconds(default – 10.0)
read_timeout (
float) – Timeout for reading response in seconds (default:300.0)
user_agent (
Optional[str]) – Custom User-Agent header for identifying application inlogs. (include in audit) – If None, defaults to ‘hfortix/{version}’. Useful for multi-team environments and troubleshooting in production.
circuit_breaker_threshold (
int) – Number of consecutive failures before(default –
circuit_breaker_timeout (
float) – Seconds to wait before transitioning to(default – 30.0)
circuit_breaker_auto_retry (
bool) – Enable automatic retry when circuit(default – False). When enabled, waits circuit_breaker_retry_delay seconds between retries instead of immediately raising exception. Useful for long-running automation scripts. NOT recommended for tests or interactive use.
circuit_breaker_max_retries (
int) – Maximum retry attempts when(default –
circuit_breaker_retry_delay (
float) – Delay in seconds between retry(default – 5.0). This is separate from circuit_breaker_timeout, which controls when the circuit transitions from open to half-open.
max_connections (
int) – Maximum number of connections in the pool(default –
max_keepalive_connections (
int) – Maximum number of keepalive connections(default –
session_idle_timeout (
Union[int,float,None]) – For username/password auth only. Idle timeoutbefore (in seconds) – proactively re-authenticating (default: 300 = 5 minutes). This should match your FortiGate’s ‘config system global’ -> ‘remoteauthtimeout’ setting. Set to None to disable proactive re-authentication. Note: API token authentication is stateless and doesn’t use sessions.
read_only (
bool) – Enable read-only mode - simulate write operations(default – False)
track_operations (
bool) – Enable operation tracking - maintain audit log of(default – False)
adaptive_retry (
bool) – Enable adaptive retry with backpressure detection(default – False). When enabled, monitors response times and adjusts retry delays based on FortiGate health signals (slow responses, 503 errors). Increases retry delays when FortiGate is overloaded to prevent cascading failures.
retry_strategy (
str) – Retry backoff strategy - ‘exponential’ (default) or ‘linear’. Exponential: 1s, 2s, 4s, 8s, 16s, 30s. Linear: 1s, 2s, 3s, 4s, 5s. Use exponential for transient failures, linear for rate limiting.retry_jitter (
bool) – Add random jitter (0-25% of delay) to retry delays to prevent thundering herd problem when multiple clients retry simultaneously (default: False).audit_handler (
Optional[Any]) – Handler for audit logging (implements AuditHandlerprotocol). – Use built-in handlers: SyslogHandler, FileHandler, StreamHandler, CompositeHandler. Essential for compliance (SOC 2, HIPAA, PCI-DSS). Example: SyslogHandler(“siem.company.com:514”)
audit_callback (
Optional[Any]) – Custom callback function for audit logging. Alternative to audit_handler. Receives operation dict as parameter. Example: lambda op: send_to_kafka(op)user_context (
Optional[dict[str,Any]]) – Optional dict with user/application context tologs. – Example: {“username”: “admin”, “app”: “automation”, “ticket”: “CHG-12345”}
- Raises:
ValueError – If parameters are invalid or both token and
username/password provided –
- Return type:
None
- login()[source]
Authenticate using username/password and obtain session token
This method is called automatically if username/password are provided during initialization. Can also be called manually to re-authenticate.
- Raises:
ValueError – If username/password not configured
AuthenticationError – If login fails
- Return type:
- logout()[source]
Logout and invalidate session token
This method is called automatically when using context manager (with statement). Can also be called manually to explicitly logout.
Note
Only applicable when using username/password authentication. Token-based authentication doesn’t require logout.
- Return type:
- get_connection_stats()[source]
Get HTTP connection pool statistics
- Returns:
- Connection statistics including:
http2_enabled: Whether HTTP/2 is enabled
max_connections: Maximum number of connections allowed
max_keepalive_connections: Maximum keepalive connections
active_requests: Current number of active requests
total_requests: Total requests made since initialization
pool_exhaustion_count: Times pool reached capacity
circuit_breaker_state: Current circuit breaker state
consecutive_failures: Number of consecutive failures
- Return type:
Example
>>> stats = client.get_connection_stats() >>> print(f"Circuit breaker: {stats['circuit_breaker_state']}") >>> print(f"Active requests: {stats['active_requests']}")
- set_transaction(transaction_id)[source]
Set active transaction ID for automatic header injection.
When a transaction ID is set, all subsequent requests will automatically include the ‘X-TRANSACTION-ID’ header required by FortiOS batch transactions.
Examples
>>> # Start transaction >>> client.set_transaction(19) >>> # All requests now include X-TRANSACTION-ID: 19 >>> >>> # Clear transaction >>> client.set_transaction(None)
- inspect_last_request()[source]
Get details of last API request for debugging
- Returns:
- Request information including:
method: HTTP method used
endpoint: API endpoint path
params: Query parameters
response_time_ms: Response time in milliseconds
status_code: HTTP status code
error: Error message if no requests made
- Return type:
Example
>>> client.get("/api/v2/cmdb/firewall/address") >>> info = client.inspect_last_request() >>> print(f"Last request took {info['response_time_ms']:.2f}ms")
- request(method, api_type, path, data=None, params=None, vdom=None, raw_json=False, request_id=None, silent=False)[source]
Generic request method for all API calls
- Parameters:
method (
str) – HTTP method (GET, POST, PUT, DELETE)api_type (
str) – API type (cmdb, monitor, log, service)path (
str) – API endpoint path (e.g., ‘firewall/address’, ‘system/status’)data (
Optional[dict[str,Any]]) – Request body data (for POST/PUT)vdom (
Union[str,bool,None]) – Virtual domain (None=use default, or specify vdom name)raw_json (
bool) – If False (default), return only ‘results’ field. If True,response (return full)
request_id (
Optional[str]) – Optional correlation ID for tracking requests acrosslogs
silent (
bool) – If True, suppress error logging (for exists() checks)
- Returns:
If raw_json=False, returns response[‘results’] (or full response if no ‘results’ key)
If raw_json=True, returns complete API response with status, http_status, etc.
- Return type:
- get(api_type, path, params=None, vdom=None, raw_json=False, silent=False)[source]
GET request
- Parameters:
- Return type:
- get_binary(api_type, path, params=None, vdom=None)[source]
GET request returning binary data (for file downloads)
- post(api_type, path, data, params=None, vdom=None, scope=None, raw_json=False)[source]
POST request - Create new object
- Parameters:
- Return type:
- put(api_type, path, data, params=None, vdom=None, scope=None, raw_json=False)[source]
PUT request - Update existing object
- Parameters:
- Return type:
- delete(api_type, path, params=None, vdom=None, scope=None, raw_json=False)[source]
DELETE request - Delete object
- Parameters:
- Return type:
- static validate_mkey(mkey, parameter_name='mkey')[source]
Validate and convert mkey to string
- Parameters:
- Return type:
- Returns:
String representation of mkey
- Raises:
ValueError – If mkey is None, empty, or invalid
Example
>>> mkey = HTTPClient.validate_mkey(user_id, 'user_id')
- static validate_required_params(params, required)[source]
Validate that required parameters are present in params dict
- Parameters:
- Raises:
ValueError – If any required parameters are missing
- Return type:
Example
>>> HTTPClient.validate_required_params(data, ['name', 'type'])
- static validate_range(value, min_val, max_val, parameter_name='value')[source]
Validate that a numeric value is within a specified range
- Parameters:
- Raises:
ValueError – If value is outside the specified range
- Return type:
Example
>>> HTTPClient.validate_range(port, 1, 65535, 'port')
- static validate_choice(value, choices, parameter_name='value')[source]
Validate that a value is one of the allowed choices
- Parameters:
- Raises:
ValueError – If value is not in the allowed choices
- Return type:
Example
>>> HTTPClient.validate_choice(protocol, ['tcp', 'udp'], 'protocol')
- static build_params(**kwargs)[source]
Build parameters dict, filtering out None values
- Parameters:
**kwargs (
Any) – Keyword arguments to build params from- Return type:
- Returns:
Dictionary with None values removed
Example
>>> params = HTTPClient.build_params(format=['name'], datasource=True, other=None) >>> # Returns: {'format': ['name'], 'datasource': True}
- close()[source]
Close the HTTP session and release resources
If using username/password authentication, this will also logout to properly clean up the session.
- Return type:
- get_operations()[source]
Get audit log of all tracked API operations
Returns all tracked operations (GET/POST/PUT/DELETE) in chronological order. Only available when track_operations=True was passed to constructor.
- Returns:
timestamp: ISO 8601 timestamp
method: HTTP method (GET/POST/PUT/DELETE)
api_type: API type (cmdb/monitor/log/service)
path: API endpoint path
data: Request payload (for POST/PUT), None otherwise
status_code: HTTP response status code
vdom: Virtual domain (if specified)
read_only_simulated: True if operation was simulated in
read-only mode
- Return type:
List of operation dictionaries with keys
Example
>>> client = HTTPClient(url="https://192.0.2.10", token="...", track_operations=True) >>> client.post("cmdb", "/firewall/address", {"name": "test"}) >>> ops = client.get_operations() >>> print(ops[0]) { 'timestamp': '2024-12-20T10:30:15Z', 'method': 'POST', 'api_type': 'cmdb', 'path': '/firewall/address', 'data': {'name': 'test'}, 'status_code': 200, 'vdom': 'root', 'read_only_simulated': False }
- get_write_operations()[source]
Get audit log of write operations only (POST/PUT/DELETE)
Filters tracked operations to return only write operations, excluding GET requests.
- Return type:
- Returns:
List of write operation dictionaries (same format as get_operations())
Example
>>> client = HTTPClient(url="https://192.0.2.10", token="...", track_operations=True) >>> client.get("cmdb", "/firewall/address/test") # GET - excluded >>> client.post("cmdb", "/firewall/address", {"name": "test2"}) # POST - included >>> client.delete("cmdb", "/firewall/address/test") # DELETE - included >>> write_ops = client.get_write_operations() >>> len(write_ops) # Returns 2 (POST and DELETE only) 2
- static make_exists_method(get_method)[source]
Create an exists() helper that works with both sync and async modes.
This utility wraps a get() method and returns a function that: - Returns True if the object exists - Returns False if ResourceNotFoundError is raised - Returns False if response has error status (e.g., {‘status’: ‘error’}) - Works transparently with both sync and async clients
- Parameters:
- Return type:
- Returns:
A function that returns bool (sync) or Coroutine[bool] (async)
Example
HTTPClientFMG (Synchronous FortiManager)
- class hfortix_core.HTTPClientFMG(url, username, password, verify=True, adom=None, max_retries=3, connect_timeout=10.0, read_timeout=300.0, circuit_breaker_threshold=5, circuit_breaker_timeout=60.0, max_connections=100, max_keepalive_connections=20, adaptive_retry=False, retry_strategy='exponential', retry_jitter=False)[source]
Bases:
BaseHTTPClientHTTP client for FortiManager JSON-RPC API.
Provides session-based authentication and JSON-RPC request handling while reusing the retry logic, circuit breaker, connection pooling, and statistics from BaseHTTPClient.
FortiManager uses a different authentication model than FortiOS: - FortiOS: REST API with Bearer token in headers - FortiManager: JSON-RPC API with session token in request body
Example
>>> client = HTTPClientFMG( ... url="https://fmg.example.com", ... username="admin", ... password="password", ... ) >>> client.login() >>> response = client.execute("get", [{"url": "/dvmdb/device"}]) >>> client.logout()
- Parameters:
url (str)
username (str)
password (str)
verify (bool)
adom (Optional[str])
max_retries (int)
connect_timeout (float)
read_timeout (float)
circuit_breaker_threshold (int)
circuit_breaker_timeout (float)
max_connections (int)
max_keepalive_connections (int)
adaptive_retry (bool)
retry_strategy (str)
retry_jitter (bool)
- __init__(url, username, password, verify=True, adom=None, max_retries=3, connect_timeout=10.0, read_timeout=300.0, circuit_breaker_threshold=5, circuit_breaker_timeout=60.0, max_connections=100, max_keepalive_connections=20, adaptive_retry=False, retry_strategy='exponential', retry_jitter=False)[source]
Initialize FortiManager HTTP client.
- Parameters:
url (
str) – Base URL for FMG (e.g., “https://fmg.example.com”)username (
str) – Admin usernamepassword (
str) – Admin passwordverify (
bool) – Verify SSL certificates (default: True)max_retries (
int) – Maximum retry attempts on transient failuresconnect_timeout (
float) – Connection timeout in secondsread_timeout (
float) – Read timeout in secondscircuit_breaker_threshold (
int) – Failures before opening circuitcircuit_breaker_timeout (
float) – Seconds before retrying after circuit opensmax_connections (
int) – Maximum connection pool sizemax_keepalive_connections (
int) – Maximum keepalive connectionsadaptive_retry (
bool) – Enable adaptive retry with backpressure detectionretry_strategy (
str) – ‘exponential’ or ‘linear’ backoffretry_jitter (
bool) – Add random jitter to retry delays
- Return type:
None
- login()[source]
Authenticate with FortiManager.
- Return type:
- Returns:
FMG login response dict with session and status information
- Raises:
RuntimeError – If authentication fails
- execute(method, params, verbose=1)[source]
Execute a FortiManager JSON-RPC request.
- Parameters:
- Return type:
- Returns:
FMG response dict
- Raises:
RuntimeError – If not authenticated or request fails
- proxy_request(action, resource, targets, payload=None, timeout=60)[source]
Execute a FortiOS API call through the FMG proxy endpoint.
This is the core method for routing FortiOS REST API calls through FortiManager to managed devices.
- Parameters:
action (
Literal['get','post','put','delete']) – HTTP method (get, post, put, delete)resource (
str) – FortiOS API resource path (e.g., “/api/v2/cmdb/firewall/address”)targets (
list[str]) – List of target devices/groups (e.g., [“adom/root/device/fw-01”])timeout (
int) – Request timeout in seconds
- Return type:
- Returns:
FMG response dict containing results from each target device
AsyncHTTPClient (Asynchronous)
- class hfortix_core.http.AsyncHTTPClient(url, verify=True, token=None, username=None, password=None, vdom=None, max_retries=3, connect_timeout=10.0, read_timeout=300.0, user_agent=None, circuit_breaker_threshold=10, circuit_breaker_timeout=30.0, circuit_breaker_auto_retry=False, circuit_breaker_max_retries=3, circuit_breaker_retry_delay=5.0, max_connections=100, max_keepalive_connections=20, session_idle_timeout=300.0, read_only=False, track_operations=False, adaptive_retry=False, retry_strategy='exponential', retry_jitter=False, audit_handler=None, audit_callback=None, user_context=None)[source]
Bases:
BaseHTTPClientInternal async HTTP client for FortiOS API requests (Async Implementation)
Implements the IHTTPClient protocol for asynchronous HTTP operations.
Async version of HTTPClient using httpx.AsyncClient. Handles all HTTP communication with FortiGate devices including: - Async session management - Authentication headers - SSL verification - Request/response handling - Error handling - Automatic retry with exponential backoff - Async context manager support (use with ‘async with’ statement)
- Protocol Implementation:
This class implements the IHTTPClient protocol, allowing it to be used interchangeably with other HTTP client implementations (e.g., HTTPClient, custom user-provided async clients). All methods return coroutines that must be awaited.
This class is internal and not exposed to users directly, but users can provide their own async IHTTPClient implementations to FortiOS.__init__().
- Parameters:
url (str)
verify (bool)
token (Optional[str])
username (Optional[str])
password (Optional[str])
vdom (Optional[str])
max_retries (int)
connect_timeout (float)
read_timeout (float)
user_agent (Optional[str])
circuit_breaker_threshold (int)
circuit_breaker_timeout (float)
circuit_breaker_auto_retry (bool)
circuit_breaker_max_retries (int)
circuit_breaker_retry_delay (float)
max_connections (int)
max_keepalive_connections (int)
session_idle_timeout (Optional[float])
read_only (bool)
track_operations (bool)
adaptive_retry (bool)
retry_strategy (str)
retry_jitter (bool)
audit_handler (Optional[Any])
audit_callback (Optional[Any])
- __init__(url, verify=True, token=None, username=None, password=None, vdom=None, max_retries=3, connect_timeout=10.0, read_timeout=300.0, user_agent=None, circuit_breaker_threshold=10, circuit_breaker_timeout=30.0, circuit_breaker_auto_retry=False, circuit_breaker_max_retries=3, circuit_breaker_retry_delay=5.0, max_connections=100, max_keepalive_connections=20, session_idle_timeout=300.0, read_only=False, track_operations=False, adaptive_retry=False, retry_strategy='exponential', retry_jitter=False, audit_handler=None, audit_callback=None, user_context=None)[source]
Initialize async HTTP client
- Parameters:
url (
str) – Base URL for API (e.g., “https://192.0.2.10”)verify (
bool) – Verify SSL certificatestoken (
Optional[str]) – API authentication token (if using token auth)username (
Optional[str]) – Username for authentication (if using username/passwordauth)
password (
Optional[str]) – Password for authentication (if using username/passwordauth)
max_retries (
int) – Maximum number of retry attempts on transient failures(default (all API calls) –
connect_timeout (
float) – Timeout for establishing connection in seconds(default – 10.0)
read_timeout (
float) – Timeout for reading response in seconds (default:300.0)
circuit_breaker_threshold (
int) – Number of consecutive failures before(default –
circuit_breaker_timeout (
float) – Seconds to wait before transitioning to(default – 30.0)
circuit_breaker_auto_retry (
bool) – When True, automatically wait and retrybreaker (when circuit) – opens instead of raising error immediately (default: False). WARNING: Not recommended for test environments - may cause long delays.
circuit_breaker_max_retries (
int) – Maximum number of auto-retry attemptsbreaker – opens (default: 3). Only used when circuit_breaker_auto_retry=True.
circuit_breaker_retry_delay (
float) – Delay in seconds between retry(default – 5.0). Separate from circuit_breaker_timeout, which controls when the circuit transitions from open to half-open.
max_connections (
int) – Maximum number of connections in the pool(default –
max_keepalive_connections (
int) – Maximum number of keepalive connections(default –
session_idle_timeout (
Optional[float]) – For username/password auth only. Idle timeoutbefore (in seconds) – proactively re-authenticating (default: 300 = 5 minutes). Set to None or False to disable. Note: Async client does not yet implement proactive re-auth; this parameter is accepted for API compatibility.
read_only (
bool) – Enable read-only mode - simulate write operations(default – False)
track_operations (
bool) – Enable operation tracking - maintain audit log of(default – False)
adaptive_retry (
bool) – Enable adaptive retry with backpressure detection(default – False). When enabled, monitors response times and adjusts retry delays based on FortiGate health signals (slow responses, 503 errors).
retry_strategy (
str) – Retry backoff strategy - ‘exponential’ (default) or ‘linear’. Exponential: 1s, 2s, 4s, 8s, 16s, 30s. Linear: 1s, 2s, 3s, 4s, 5s.retry_jitter (
bool) – Add random jitter (0-25% of delay) to retry delays to prevent thundering herd problem (default: False).audit_handler (
Optional[Any]) – Handler for audit logging (implements AuditHandlerprotocol). – Use built-in handlers: SyslogHandler, FileHandler, StreamHandler, CompositeHandler. Essential for compliance (SOC 2, HIPAA, PCI-DSS).
audit_callback (
Optional[Any]) – Custom callback function for audit logging. Alternative to audit_handler. Receives operation dict as parameter.user_context (
Optional[dict[str,Any]]) – Optional dict with user/application context tologs. (include in audit) – Example: {“username”: “admin”, “app”: “automation”, “ticket”: “CHG-12345”}
- Raises:
ValueError – If parameters are invalid or both token and
username/password provided –
- Return type:
None
- async login()[source]
Authenticate using username/password and obtain session token (async)
Must be called manually for async clients (cannot be called in __init__). Alternatively, use async context manager which handles login/logout automatically.
- Raises:
ValueError – If username/password not configured
AuthenticationError – If login fails
- Return type:
Example
>>> client = AsyncHTTPClient(url, username="admin", password="password") >>> await client.login()
- async logout()[source]
Logout and invalidate session token (async)
This method is called automatically when using async context manager. Can also be called manually to explicitly logout.
Note
Only applicable when using username/password authentication. Token-based authentication doesn’t require logout.
- Return type:
- get_connection_stats()[source]
Get HTTP connection pool statistics (async client)
- Returns:
- Connection statistics including:
http2_enabled: Whether HTTP/2 is enabled
max_connections: Maximum number of connections allowed
max_keepalive_connections: Maximum keepalive connections
active_requests: Current number of active requests
total_requests: Total requests made since initialization
pool_exhaustion_count: Times pool reached capacity
circuit_breaker_state: Current circuit breaker state
consecutive_failures: Number of consecutive failures
- Return type:
Example
>>> stats = client.get_connection_stats() >>> print(f"Circuit breaker: {stats['circuit_breaker_state']}") >>> print(f"Active requests: {stats['active_requests']}")
- inspect_last_request()[source]
Get details of last API request for debugging
- Returns:
- Request information including:
method: HTTP method used
endpoint: API endpoint path
params: Query parameters
response_time_ms: Response time in milliseconds
status_code: HTTP status code
error: Error message if no requests made
- Return type:
Example
>>> await client.get("/api/v2/cmdb/firewall/address") >>> info = client.inspect_last_request() >>> print(f"Last request took {info['response_time_ms']:.2f}ms")
- async request(method, api_type, path, data=None, params=None, vdom=None, raw_json=False, request_id=None)[source]
Generic async request method for all API calls
- Parameters:
method (
str) – HTTP method (GET, POST, PUT, DELETE)api_type (
str) – API type (cmdb, monitor, log, service)path (
str) – API endpoint pathdata (
Optional[dict[str,Any]]) – Request body data (for POST/PUT)raw_json (
bool) – If False, return only ‘results’ field. If True, returnresponse (full)
request_id (
Optional[str]) – Optional correlation ID for tracking requests
- Returns:
API response (results or full response based on raw_json)
- Return type:
- async get_binary(api_type, path, params=None, vdom=None)[source]
Async GET request returning binary data
- async post(api_type, path, data, params=None, vdom=None, raw_json=False)[source]
Async POST request - Create new object
- async put(api_type, path, data, params=None, vdom=None, raw_json=False)[source]
Async PUT request - Update existing object
- async delete(api_type, path, params=None, vdom=None, raw_json=False)[source]
Async DELETE request - Delete object
- static validate_required_params(params, required)[source]
Validate that required parameters are present
- static validate_range(value, min_val, max_val, parameter_name='value')[source]
Validate that a numeric value is within a specified range
- static validate_choice(value, choices, parameter_name='value')[source]
Validate that a value is one of the allowed choices
- async close()[source]
Close the async HTTP session and release resources
This method should be called to properly clean up resources when using AsyncHTTPClient. It ensures that all network connections and sessions are closed.
- Return type:
- Usage:
Call await client.close() when you are done with the client in
async mode. - Prefer using the async context manager (async with) for automatic cleanup.
Example
client = AsyncHTTPClient(…) try:
…
- finally:
await client.close()
- get_operations()[source]
Get audit log of all tracked API operations
Returns all tracked operations (GET/POST/PUT/DELETE) in chronological order. Only available when track_operations=True was passed to constructor.
- get_write_operations()[source]
Get audit log of write operations only (POST/PUT/DELETE)
Filters tracked operations to return only write operations, excluding GET requests.
- static make_exists_method(get_method)[source]
Create an exists() helper that works with both sync and async modes.
This utility wraps a get() method and returns a function that: - Returns True if the object exists - Returns False if ResourceNotFoundError is raised - Works transparently with both sync and async clients
- Parameters:
- Return type:
- Returns:
A function that returns bool (sync) or Coroutine[bool] (async)
Example
Caching
TTLCache
- class hfortix_core.TTLCache(default_ttl=3600)[source]
Bases:
Generic[T]Simple TTL-based cache for readonly reference data.
Thread-safe in-memory cache with time-based expiration. Designed for caching readonly reference tables that rarely change.
Example
>>> cache = TTLCache[dict](default_ttl=3600) # 1 hour >>> cache.set("geography/countries", country_data) >>> data = cache.get("geography/countries")
- Parameters:
default_ttl (float)
- __init__(default_ttl=3600)[source]
Initialize TTL cache.
- Parameters:
default_ttl (
float) – Default time-to-live in seconds (default: 1 hour)
readonly_cache
- hfortix_core.readonly_cache = <hfortix_core.cache.TTLCache object>
Simple TTL-based cache for readonly reference data.
Thread-safe in-memory cache with time-based expiration. Designed for caching readonly reference tables that rarely change.
Example
>>> cache = TTLCache[dict](default_ttl=3600) # 1 hour >>> cache.set("geography/countries", country_data) >>> data = cache.get("geography/countries")
Logging
RequestLogger
- class hfortix_core.RequestLogger(method, endpoint, extra=None)[source]
Bases:
objectContext manager for logging API requests with timing and status
Automatically logs request start/completion and calculates duration. Logs errors with full context if the request fails.
Example
>>> with RequestLogger("POST", "/api/v2/cmdb/firewall/address", extra={"vdom": "root"}): # noqa: E501 ... response = make_request() # Logs: ✓ POST /api/v2/cmdb/firewall/address (0.234s) {vdom: root}
log_operation
- hfortix_core.log_operation(logger_name, operation, level='INFO', **kwargs)[source]
Log an operation with structured data
- Parameters:
- Return type:
Example
>>> log_operation( ... "hfortix.client", ... "Creating firewall address", ... level="INFO", ... address_name="server1", ... subnet="10.0.0.1/32", ... vdom="root" ... ) # Logs: Creating firewall address {address_name: server1, subnet: 10.0.0.1/32, vdom: root} # noqa: E501
StructuredFormatter
- class hfortix_core.StructuredFormatter(include_fields=None, exclude_fields=None)[source]
Bases:
FormatterFormat log records as structured JSON
Useful for log aggregation systems like ELK, Splunk, CloudWatch that can parse JSON logs for better searching and analysis.
- Standard Fields (always present):
timestamp: ISO 8601 UTC timestamp
level: Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
logger: Logger name (e.g., “hfortix.http.client”)
message: Log message
- Common Extra Fields (added via extra={…}):
request_id: Unique request identifier for correlation
method: HTTP method (GET, POST, PUT, DELETE, PATCH)
endpoint: API endpoint path
status_code: HTTP status code
duration_seconds: Request duration in seconds
vdom: FortiOS Virtual Domain (multi-tenant environments)
adom: FortiManager/FortiAnalyzer Administrative Domain
error_type: Exception class name (for errors)
attempt: Current retry attempt number
max_attempts: Maximum retry attempts
Example
>>> import logging >>> from hfortix_core.logging import StructuredFormatter >>> >>> handler = logging.StreamHandler() >>> handler.setFormatter(StructuredFormatter()) >>> logger = logging.getLogger("hfortix") >>> logger.addHandler(handler) >>> >>> # Basic usage >>> logger.info("API request completed", ... extra={"endpoint": "/api/v2/cmdb/firewall/policy", ... "duration_seconds": 0.145})
Output: {“timestamp”:”2026-01-02T14:23:45.123Z”,”level”:”INFO”,
“logger”:”hfortix”,”message”:”API request completed”, “endpoint”:”/api/v2/cmdb/firewall/policy”,”duration_seconds”:0.145}
>>> # Multi-tenant usage >>> logger.info("Policy created", ... extra={"vdom": "customer_a", "endpoint": "/api/v2/cmdb/firewall/policy", # noqa: E501 ... "request_id": "req-123", "status_code": 200})
Output: {“timestamp”:”2026-01-02T14:23:45.456Z”,”level”:”INFO”,
“logger”:”hfortix”,”message”:”Policy created”,”vdom”:”customer_a”, “endpoint”:”/api/v2/cmdb/firewall/policy”,”request_id”:”req-123”, “status_code”:200}
TextFormatter
- class hfortix_core.TextFormatter(use_color=False)[source]
Bases:
FormatterFormat log records as human-readable text
Provides colorized output for terminal and clean formatting for log files.
Example
>>> import logging >>> from hfortix_core.logging import TextFormatter >>> >>> handler = logging.StreamHandler() >>> handler.setFormatter(TextFormatter(use_color=True)) >>> logger = logging.getLogger("hfortix") >>> logger.addHandler(handler) >>> logger.info("API request completed")
Output: 2026-01-02 14:23:45 [INFO] hfortix: API request completed
- Parameters:
use_color (bool)
- COLORS = {'CRITICAL': '\x1b[35m', 'DEBUG': '\x1b[36m', 'ERROR': '\x1b[31m', 'INFO': '\x1b[32m', 'RESET': '\x1b[0m', 'WARNING': '\x1b[33m'}
LogFormatter (Protocol)
LogRecord (TypedDict)
- class hfortix_core.logging.LogRecord[source]
Bases:
TypedDictType definition for structured log record data
- timestamp
ISO 8601 UTC timestamp
- level
Log level (DEBUG, INFO, WARNING, ERROR, CRITICAL)
- logger
Logger name (e.g., “hfortix.http.client”)
- message
Log message
- request_id
Unique request identifier for correlation
- method
HTTP method (GET, POST, PUT, DELETE)
- endpoint
API endpoint path
- status_code
HTTP status code
- duration_s
Request duration in seconds
- duration_ms
Request duration in milliseconds
- vdom
FortiOS Virtual Domain
- event
Event type (request_start, request_completed, request_failed)
- error
Error message (if applicable)
- error_type
Exception class name
- attempt
Current retry attempt number
- max_attempts
Maximum retry attempts
- source
Source location (file, line, function)
Debugging
DebugSession
- class hfortix_core.DebugSession(client, capture_response_data=False, print_on_exit=True)[source]
Bases:
objectContext manager for debugging FortiOS API sessions.
Captures detailed information about all API requests made within the session, # noqa: E501 including timing, connection stats, and request/response details.
- Parameters:
client (FortiOS) – FortiOS client instance to debug
capture_response_data (bool) – Whether to capture full response bodies (default: False) # noqa: E501
print_on_exit (bool) – Whether to print summary when exiting context (default: True) # noqa: E501
Example
>>> with DebugSession(fgt, capture_response_data=True) as session: ... fgt.cmdb.firewall.address.get() ... fgt.cmdb.firewall.policy.create(data={...}) ... session.print_summary()
Session Summary:
Duration: 1.234s Total Requests: 2 Successful: 2 Failed: 0 Average Response Time: 617ms …
- __init__(client, capture_response_data=False, print_on_exit=True)[source]
Initialize debug session.
- Parameters:
client (FortiOS) – FortiOS client to monitor
capture_response_data (bool) – Capture full response bodies
print_on_exit (bool) – Auto-print summary on context exit
- Return type:
None
- capture_request(method, endpoint, params=None, response_time_ms=None, status_code=None, response_data=None, error=None)[source]
Capture information about an API request.
debug_timer
- hfortix_core.debug_timer(operation='Operation')[source]
Context manager for timing operations.
- Parameters:
operation (
str) – Name of the operation being timed- Yields:
Dictionary that will be populated with timing information
- Return type:
Example
>>> with debug_timer("Get firewall addresses") as timing: ... result = fgt.cmdb.firewall.address.get() >>> print(f"Took {timing['duration_ms']:.1f}ms")
format_connection_stats
- hfortix_core.format_connection_stats(stats)[source]
Format connection stats dictionary as human-readable string.
- Parameters:
stats (
dict[str,Any] |None) – Connection stats from client.connection_stats- Return type:
- Returns:
Formatted string with connection statistics
Example
>>> stats = fgt.connection_stats >>> print(format_connection_stats(stats)) Connection Pool Statistics ========================== Max Connections: 100 Active Requests: 2 Total Requests: 1543 Pool Exhaustion Events: 0
format_request_info
- hfortix_core.format_request_info(request_info)[source]
Format request info dictionary as human-readable string.
- Parameters:
request_info (
dict[str,Any] |None) – Request info from client.last_request- Return type:
- Returns:
Formatted string with request details
Example
>>> info = fgt.last_request >>> print(format_request_info(info)) GET /api/v2/cmdb/firewall/address Response Time: 123.4ms Status Code: 200
print_debug_info
- hfortix_core.print_debug_info(client, include_last_request=True, include_connection_stats=True)[source]
Print comprehensive debug information for a FortiOS client.
- Parameters:
client (FortiOS) – FortiOS client to inspect
include_last_request (bool) – Include last request details
include_connection_stats (bool) – Include connection pool statistics
- Return type:
None
Example
>>> from hfortix_core.debug import print_debug_info >>> fgt = FortiOS(host="192.168.1.99", token="your-token") >>> fgt.cmdb.firewall.address.get() >>> print_debug_info(fgt)
DebugFormatter (Protocol)
DebugInfo (TypedDict)
- class hfortix_core.debug.DebugInfo[source]
Bases:
TypedDictType definition for comprehensive debug information
- last_request
Information about the last API request
- connection_stats
Connection pool statistics
- session_active
Whether a debug session is active
- capture_enabled
Whether response capture is enabled
SessionSummary (TypedDict)
- class hfortix_core.debug.SessionSummary[source]
Bases:
TypedDictType definition for debug session summary
- duration_seconds
Total session duration in seconds
- total_requests
Total number of requests made
- successful_requests
Number of successful requests
- failed_requests
Number of failed requests
- avg_response_time_ms
Average response time in milliseconds
- max_response_time_ms
Maximum response time in milliseconds
- min_response_time_ms
Minimum response time in milliseconds
- stats_delta
Change in connection stats during session
- initial_stats
Connection stats at session start
- final_stats
Connection stats at session end
Formatting
fmt Module
Data formatting utilities for FortiOS objects and data structures.
Provides simple, type-agnostic conversion functions that handle any input gracefully. Never raises exceptions - returns sensible defaults for edge cases.
- hfortix_core.fmt.to_json(data, indent=2, **kwargs)[source]
Convert any data to formatted JSON string.
Handles objects with __dict__, converts sets/tuples to lists, and uses str() fallback for non-serializable types.
- hfortix_core.fmt.to_csv(data, separator=', ')[source]
Convert any data to comma-separated string.
- Parameters:
- Return type:
- Returns:
Comma-separated string
Examples
>>> to_csv(['port1', 'port2', 'port3']) 'port1, port2, port3'
>>> to_csv({'x': 1, 'y': 2, 'z': 3}) 'x=1, y=2, z=3'
>>> to_csv('already a string') 'already a string'
>>> to_csv(None) ''
>>> to_csv([1, 2, 3], separator=' | ') '1 | 2 | 3'
>>> class Interface: ... def __init__(self): ... self.name = "port1" ... self.ip = "10.0.0.1" >>> to_csv(Interface()) 'name=port1, ip=10.0.0.1'
- hfortix_core.fmt.to_dict(data)[source]
Convert any data to dictionary.
Returns a dict with string keys for most inputs (objects, existing dicts), or integer keys for list/tuple inputs.
- Parameters:
data (
Any) – Any Python object to convert- Return type:
- Returns:
Dictionary representation of the data
Examples
>>> class Policy: ... def __init__(self): ... self.name = "Allow-All" ... self.action = "accept" >>> to_dict(Policy()) {'name': 'Allow-All', 'action': 'accept'}
>>> to_dict({'already': 'a dict'}) {'already': 'a dict'}
>>> to_dict([('a', 1), ('b', 2)]) {'a': 1, 'b': 2}
>>> to_dict(['x', 'y', 'z']) {0: 'x', 1: 'y', 2: 'z'}
>>> to_dict('simple string') {'value': 'simple string'}
>>> to_dict(None) {'value': None}
- hfortix_core.fmt.to_multiline(data, separator='\\n')[source]
Convert any data to newline-separated string.
- Parameters:
- Return type:
- Returns:
Newline-separated string
Examples
>>> print(to_multiline(['port1', 'port2', 'port3'])) port1 port2 port3
>>> print(to_multiline({'name': 'policy1', 'action': 'accept'})) name: policy1 action: accept
>>> to_multiline('already a string') 'already a string'
>>> to_multiline(None) ''
>>> class Policy: ... def __init__(self): ... self.name = "Allow-All" ... self.policyid = 1 >>> print(to_multiline(Policy())) name: Allow-All policyid: 1
- hfortix_core.fmt.to_list(data, delimiter=None)[source]
Convert any data to list.
- Parameters:
- Return type:
- Returns:
List representation of the data
Examples
>>> to_list(['already', 'a', 'list']) ['already', 'a', 'list']
>>> to_list(('tuple', 'to', 'list')) ['tuple', 'to', 'list']
>>> to_list({'a', 'b', 'c'}) # set to list ['a', 'b', 'c']
>>> to_list('port1,port2,port3', delimiter=',') ['port1', 'port2', 'port3']
>>> to_list('port1 port2 port3') # auto-splits on space ['port1', 'port2', 'port3']
>>> to_list('80 443 8080') # works with numbers as strings ['80', '443', '8080']
>>> to_list('port1 | port2 | port3', delimiter=' | ') ['port1', 'port2', 'port3']
>>> to_list('single_string') # no spaces, returns as-is ['single_string']
>>> to_list({'name': 'policy1', 'action': 'accept'}) ['policy1', 'accept']
>>> to_list(None) []
>>> to_list(42) [42]
>>> class Policy: ... def __init__(self): ... self.name = "Allow-All" ... self.policyid = 1 >>> to_list(Policy()) ['Allow-All', 1]
- hfortix_core.fmt.to_quoted(data, quote='"', separator=', ')[source]
Convert any data to quoted string representation.
- Parameters:
- Return type:
- Returns:
Quoted string representation
Examples
>>> to_quoted(['port1', 'port2', 'port3']) '"port1", "port2", "port3"'
>>> to_quoted({'x': 1, 'y': 2}) '"x", "y"'
>>> to_quoted('hello') '"hello"'
>>> to_quoted(None) '""'
>>> to_quoted([1, 2, 3], quote="'") "'1', '2', '3'"
>>> class Interface: ... def __init__(self): ... self.name = "port1" ... self.vlan = 10 >>> to_quoted(Interface()) '"name", "vlan"'
- hfortix_core.fmt.to_table(data, headers=True, delimiter=' | ')[source]
Convert data to table format.
- Parameters:
- Return type:
- Returns:
Table-formatted string
Examples
>>> policies = [ ... {'name': 'Allow-Web', 'action': 'accept', 'policyid': 1}, ... {'name': 'Block-All', 'action': 'deny', 'policyid': 2} ... ] >>> print(to_table(policies)) name | action | policyid Allow-Web | accept | 1 Block-All | deny | 2
>>> to_table(policies, headers=False) 'Allow-Web | accept | 1\nBlock-All | deny | 2'
>>> to_table(policies, delimiter=' || ') 'name || action || policyid\nAllow-Web || accept || 1\nBlock-All || deny || 2'
- hfortix_core.fmt.to_yaml(data, indent=2)[source]
Convert data to YAML-like format (simple, no external dependencies).
- Parameters:
- Return type:
- Returns:
YAML-style string
Examples
>>> policy = {'name': 'Allow-Web', 'action': 'accept', 'srcintf': ['port1', 'port2']} >>> print(to_yaml(policy)) name: Allow-Web action: accept srcintf: - port1 - port2
>>> print(to_yaml({'nested': {'key': 'value'}})) nested: key: value
- hfortix_core.fmt.to_xml(data, root='data', indent=2)[source]
Convert data to simple XML format (no external dependencies).
- Parameters:
- Return type:
- Returns:
XML string
Examples
>>> policy = {'name': 'Allow-Web', 'policyid': 1} >>> print(to_xml(policy, root='policy')) <policy> <name>Allow-Web</name> <policyid>1</policyid> </policy>
>>> policies = [{'name': 'p1'}, {'name': 'p2'}] >>> print(to_xml(policies, root='policies')) <policies> <item> <name>p1</name> </item> <item> <name>p2</name> </item> </policies>
- hfortix_core.fmt.to_key_value(data, separator='=', delimiter='\\n')[source]
Convert data to key=value pairs format.
- Parameters:
- Return type:
- Returns:
Key-value pairs string
Examples
>>> config = {'host': '192.168.1.1', 'port': 443, 'verify': False} >>> print(to_key_value(config)) host=192.168.1.1 port=443 verify=False
>>> to_key_value(config, separator=': ', delimiter='; ') 'host: 192.168.1.1; port: 443; verify: False'
- hfortix_core.fmt.to_markdown_table(data)[source]
Convert data to Markdown table format.
- Parameters:
data (
Any) – List of dicts or list of objects- Return type:
- Returns:
Markdown table string
Examples
>>> policies = [ ... {'name': 'Allow-Web', 'action': 'accept'}, ... {'name': 'Block-All', 'action': 'deny'} ... ] >>> print(to_markdown_table(policies)) | name | action | | --- | --- | | Allow-Web | accept | | Block-All | deny |
- hfortix_core.fmt.to_dictlist(data)[source]
Convert dict of lists to list of dicts (columnar to row format).
Useful for transforming columnar data into row-based records.
- Parameters:
data (
Any) – Dict where values are lists, or any convertible data- Return type:
- Returns:
List of dicts where each dict is one row
Examples
>>> columnar = {'name': ['p1', 'p2'], 'action': ['accept', 'deny']} >>> to_dictlist(columnar) [{'name': 'p1', 'action': 'accept'}, {'name': 'p2', 'action': 'deny'}]
>>> to_dictlist({'ports': ['80', '443', '8080']}) [{'ports': '80'}, {'ports': '443'}, {'ports': '8080'}]
>>> # Already list of dicts - returns as-is >>> to_dictlist([{'name': 'p1'}, {'name': 'p2'}]) [{'name': 'p1'}, {'name': 'p2'}]
>>> to_dictlist(None) []
- hfortix_core.fmt.to_listdict(data)[source]
Convert list of dicts to dict of lists (row to columnar format).
Useful for transforming row-based records into columnar data.
- Parameters:
data (
Any) – List of dicts, list of objects, or any convertible data- Return type:
- Returns:
Dict where keys are field names and values are lists
Examples
>>> rows = [{'name': 'p1', 'action': 'accept'}, {'name': 'p2', 'action': 'deny'}] >>> to_listdict(rows) {'name': ['p1', 'p2'], 'action': ['accept', 'deny']}
>>> to_listdict([{'ports': '80'}, {'ports': '443'}, {'ports': '8080'}]) {'ports': ['80', '443', '8080']}
>>> # Single dict becomes dict of single-item lists >>> to_listdict({'name': 'p1', 'action': 'accept'}) {'name': ['p1'], 'action': ['accept']}
>>> to_listdict(None) {}
Audit Logging
Enterprise-grade audit logging for compliance and security monitoring.
AuditHandler (Protocol)
- class hfortix_core.audit.AuditHandler(*args, **kwargs)[source]
Bases:
ProtocolProtocol for audit log handlers
Any class implementing this protocol can be used as an audit handler. This allows for maximum flexibility - users can provide custom handlers for their specific logging infrastructure.
The handler receives sanitized operation data and is responsible for: - Formatting the data as needed - Sending to destination (file, syslog, database, etc.) - Handling errors gracefully (should not raise exceptions)
Example
>>> class CustomHandler: ... def log_operation(self, operation: dict[str, Any]) -> None: ... # Send to your logging infrastructure ... send_to_kafka(operation) ... update_database(operation) ... >>> handler = CustomHandler() >>> fgt = FortiOS("192.168.1.99", token="...", audit_handler=handler)
- log_operation(operation)[source]
Log an API operation
- Parameters:
operation (
dict[str,Any]) – Dictionary containing operation details (see AuditOperation) # noqa: E501- Return type:
Note
This method should handle errors internally and not raise exceptions, # noqa: E501 as audit logging failures should not break API operations.
SyslogHandler
- class hfortix_core.audit.SyslogHandler(server, formatter=None, timeout=5.0)[source]
Bases:
objectSend audit logs to a syslog server
Sends UDP packets to a syslog server in RFC 5424 format. Commonly used for SIEM integration (Splunk, ELK, QRadar, ArcSight).
Example
>>> from hfortix_core.audit import SyslogHandler >>> handler = SyslogHandler("siem.company.com:514") >>> # Use with FortiOS client >>> fgt = FortiOS("192.168.1.99", token="...", audit_handler=handler)
Note
Uses UDP protocol which is fire-and-forget. For guaranteed delivery, consider using FileHandler with external log shipping.
- Parameters:
server (str)
formatter (AuditFormatter | None)
timeout (float)
- __init__(server, formatter=None, timeout=5.0)[source]
Initialize Syslog handler
- Parameters:
server (
str) – Syslog server in format “host:port” or “host” (default port 514) # noqa: E501formatter (
AuditFormatter|None) – Custom formatter (default: SyslogFormatter)timeout (
float) – Socket timeout in seconds
FileHandler
- class hfortix_core.audit.FileHandler(filepath, max_bytes=10000000, backup_count=5, formatter=None, mode='a')[source]
Bases:
objectWrite audit logs to a file
Writes audit logs to a file in JSON Lines format (one JSON object per line). # noqa: E501 Supports automatic log rotation based on file size.
Example
>>> from hfortix_core.audit import FileHandler >>> handler = FileHandler("/var/log/fortinet-audit.jsonl") >>> fgt = FortiOS("192.168.1.99", token="...", audit_handler=handler)
- File Format:
Each line is a complete JSON object (JSON Lines format): {“timestamp”:”2026-01-02T14:23:45Z”,”method”:”POST”,…} {“timestamp”:”2026-01-02T14:23:46Z”,”method”:”GET”,…}
- This format is:
Easy to parse (one record per line)
Compatible with log shipping tools (Fluentd, Logstash, Filebeat)
Human-readable with tools like jq
- Parameters:
filepath (str | Path)
max_bytes (int)
backup_count (int)
formatter (AuditFormatter | None)
mode (str)
- __init__(filepath, max_bytes=10000000, backup_count=5, formatter=None, mode='a')[source]
Initialize File handler
- Parameters:
StreamHandler
- class hfortix_core.audit.StreamHandler(stream=None, formatter=None)[source]
Bases:
objectWrite audit logs to a stream (stdout/stderr)
Useful for containerized applications where logs are captured by container orchestration (Docker, Kubernetes) and sent to centralized logging (CloudWatch, Datadog, etc.).
Example
>>> from hfortix_core.audit import StreamHandler >>> import sys >>> >>> # Log to stdout (captured by Docker/Kubernetes) >>> handler = StreamHandler(sys.stdout, format="json") >>> fgt = FortiOS("192.168.1.99", token="...", audit_handler=handler)
- Parameters:
stream (TextIO | None)
formatter (AuditFormatter | None)
CompositeHandler
- class hfortix_core.audit.CompositeHandler(handlers, aggregate_errors=True, error_threshold=10)[source]
Bases:
objectSend audit logs to multiple handlers with advanced routing
Features: - Multiple destination routing - Priority-based ordering - Conditional routing with filters - Error aggregation and reporting - Individual handler enable/disable
- Example (Basic):
>>> from hfortix_core.audit import CompositeHandler, SyslogHandler, FileHandler # noqa: E501 >>> >>> # Send to both SIEM and local file >>> handler = CompositeHandler([ ... SyslogHandler("siem.company.com:514"), # Compliance ... FileHandler("/var/log/fortinet.log"), # Debugging/backup ... ]) >>> >>> fgt = FortiOS("192.168.1.99", token="...", audit_handler=handler)
- Example (Priority and Filtering):
>>> # Higher priority handlers execute first >>> handler = CompositeHandler([ ... (critical_handler, 10, lambda op: op['action'] == 'delete'), ... (normal_handler, 5, lambda op: op['success']), ... (all_handler, 1, None), # No filter, always executes ... ])
- Error Handling:
If one handler fails, others continue. Errors are aggregated and can be retrieved via error_summary property.
- __init__(handlers, aggregate_errors=True, error_threshold=10)[source]
Initialize Composite handler
- Parameters:
handlers (
list[Any]) – List of handlers or tuples (handler, priority, filter_fn) - handler: Implements AuditHandler protocol - priority: Int (higher = executes first), default: 0 - filter_fn: Callable[[dict], bool] or None, default: Noneaggregate_errors (
bool) – Track errors for reportingerror_threshold (
int) – Max errors to track per handler
- property error_summary: dict[str, Any]
Get summary of handler errors
- Returns:
Dictionary with error counts and samples per handler
NullHandler
- class hfortix_core.audit.NullHandler[source]
Bases:
objectNull handler that does nothing
Useful for disabling audit logging without changing code.
Example
>>> from hfortix_core.audit import NullHandler >>> >>> # Disable audit logging >>> handler = NullHandler() >>> fgt = FortiOS("192.168.1.99", token="...", audit_handler=handler)
AuditFormatter (Protocol)
JSONFormatter
- class hfortix_core.audit.JSONFormatter(pretty=False, indent=2)[source]
Bases:
objectFormat audit logs as JSON (default)
Outputs compact JSON on a single line, suitable for log aggregation systems like ELK, Splunk, or cloud logging services.
- Example output:
{“timestamp”:”2026-01-02T14:23:45Z”,”method”:”POST”,…}
SyslogFormatter
- class hfortix_core.audit.SyslogFormatter(facility='LOCAL0', severity='INFO', app_name='hfortix', hostname=None)[source]
Bases:
objectFormat audit logs for Syslog (RFC 5424)
Outputs Syslog-formatted messages with proper priority, timestamp, and structured data.
- Format:
<PRI>VERSION TIMESTAMP HOSTNAME APP-NAME PROCID MSGID STRUCTURED-DATA MSG # noqa: E501
- Example output:
<134>1 2026-01-02T14:23:45Z 192.168.1.99 hfortix - - - {“method”:”POST”,…} # noqa: E501
- FACILITIES = {'AUTH': 4, 'AUTHPRIV': 10, 'CRON': 9, 'DAEMON': 3, 'FTP': 11, 'KERN': 0, 'LOCAL0': 16, 'LOCAL1': 17, 'LOCAL2': 18, 'LOCAL3': 19, 'LOCAL4': 20, 'LOCAL5': 21, 'LOCAL6': 22, 'LOCAL7': 23, 'LPR': 6, 'MAIL': 2, 'NEWS': 7, 'SYSLOG': 5, 'USER': 1, 'UUCP': 8}
- SEVERITIES = {'ALERT': 1, 'CRIT': 2, 'DEBUG': 7, 'EMERG': 0, 'ERR': 3, 'INFO': 6, 'NOTICE': 5, 'WARNING': 4}
CEFFormatter
- class hfortix_core.audit.CEFFormatter(device_vendor='Fortinet', device_product='FortiGate', device_version='7.0')[source]
Bases:
objectFormat audit logs as Common Event Format (CEF)
CEF is widely used by SIEM systems like ArcSight, Splunk, QRadar.
- Format:
CEF:Version|Device Vendor|Device Product|Device Version|Signature ID|Name|Severity|Extension # noqa: E501
- Example output:
CEF:0|Fortinet|FortiGate|7.0|API_OPERATION|FortiGate API Operation|5| act=POST dst=192.168.1.99 suser=api-token outcome=success …
- SEVERITY_MAP = {'DELETE': 7, 'GET': 2, 'POST': 5, 'PUT': 5}
AuditOperation (TypedDict)
- class hfortix_core.audit.AuditOperation[source]
Bases:
TypedDictType definition for audit operation data
- timestamp
ISO 8601 timestamp when operation occurred
- request_id
Unique identifier for correlating related operations
- method
HTTP method (GET, POST, PUT, DELETE)
- endpoint
Full API endpoint path (e.g., ‘/api/v2/cmdb/firewall/policy’)
- api_type
API category (cmdb, monitor, log, service)
- path
Relative path within api_type
- vdom
Virtual domain name (if applicable)
- action
High-level action (create, update, delete, read, list)
- object_type
Type of object being operated on (e.g., ‘firewall.policy’)
- object_name
Name/identifier of specific object (if available)
- data
Request payload (sanitized)
- params
Query parameters (sanitized)
- status_code
HTTP response status code
- success
Whether operation succeeded
- duration_ms
Operation duration in milliseconds
- error
Error message (if operation failed)
- user_context
Optional user-provided context dict
- host
FortiGate device IP/hostname
- read_only_mode
Whether operation was simulated in read-only mode
Request Hooks
Protocol-based hooks for intercepting and modifying API requests/responses.
BeforeRequestHook (Protocol)
- class hfortix_core.hooks.BeforeRequestHook(*args, **kwargs)[source]
Bases:
ProtocolProtocol for before-request hooks
Hooks implementing this protocol are called BEFORE sending API requests. They can: - Validate request data - Transform request data - Add headers or parameters - Cancel requests (raise exception) - Log/audit request details
Example
>>> class ValidationHook: ... def before_request( ... self, context: dict[str, Any] ... ) -> dict[str, Any]: ... # Validate request ... if 'ticket' not in context.get('user_context', {}): ... raise ValueError("Change ticket required!") ... return context ... >>> hook = ValidationHook() >>> fgt = FortiOS( ... "192.168.1.99", token="...", before_request_hooks=[hook] ... )
- before_request(context)[source]
Called before sending API request
- Parameters:
context (
dict[str,Any]) – Request context (see RequestContext)- Return type:
- Returns:
Modified context (changes apply to request)
- Raises:
Any exception to cancel the request –
Note
Modify context[‘data’] or context[‘params’] to change request
Raise exceptions to block requests
Keep execution fast - this runs in request path
AfterRequestHook (Protocol)
- class hfortix_core.hooks.AfterRequestHook(*args, **kwargs)[source]
Bases:
ProtocolProtocol for after-request hooks
Hooks implementing this protocol are called AFTER receiving API responses. They can: - Validate responses - Transform response data - Log successful operations - Trigger side effects - Cache results
Note: The existing audit_handler is a specialized after-request hook.
Example
>>> class CacheHook: ... def after_request( ... self, context: dict[str, Any], response: dict[str, Any] ... ) -> dict[str, Any]: ... # Cache GET responses ... if context['method'] == 'GET': ... self.cache[context['endpoint']] = response ... return response
- after_request(context, response)[source]
Called after receiving API response (success only)
- Parameters:
- Return type:
- Returns:
Modified response (changes apply to caller)
Note
Only called on successful requests (2xx status)
Exceptions in hooks don’t cancel the response
Keep execution fast - this runs in response path
RequestContext (TypedDict)
- class hfortix_core.hooks.RequestContext[source]
Bases:
TypedDictRequest context passed to hooks
- method
HTTP method (GET, POST, PUT, DELETE)
- api_type
API category (cmdb, monitor, log, service)
- path
Relative endpoint path
- data
Request payload (mutable)
- params
Query parameters (mutable)
- vdom
Virtual domain
- endpoint
Full endpoint path
- request_id
Unique request identifier
- user_context
User-provided context metadata