Skip to main content

FortiZTP Cloud API Client - Device provisioning and management

Project description

FortiZTP Package

Python SDK for FortiZTP Cloud API v2.0 - Device provisioning and management for FortiGate, FortiAP, FortiSwitch, and FortiExtender.

Status

✅ FUNCTIONAL - BETA RELEASE

Schema Completion: 100% (18/18 endpoints)

  • ✅ Device endpoints: 5/5 complete
  • ✅ Script endpoints: 7/7 complete
  • ✅ FortiManager endpoints: 5/5 complete
  • ✅ System endpoint: 1/1 complete

Code Generation: ✅ Complete - All 18 endpoints auto-generated from schema

Testing: ✅ Live tests passing - GET /v2/devices validated with real API

See dev/SCHEMA_GAPS_ANALYSIS.md for details on missing information.


Overview

This package will provide a fully typed Python SDK for FortiZTP Cloud API with:

  • 🔒 OAuth 2.0 Authentication - Automatic token management
  • 📝 Full Type Safety - Type hints with Literal types for autocomplete
  • 🎯 Specialized Responses - Property access on response objects
  • 🔄 Smart Defaults - Sensible defaults for optional parameters
  • HTTP Metadata - Status codes, response times, raw access
  • 🛡️ Error Handling - Comprehensive exception handling

Quick Start

Installation

pip install hfortix-fortiztp

Basic Usage

from hfortix_fortiztp import FortiZTP

# Option 1: Auto-login with credentials
client = FortiZTP(
    api_id="your_api_id",
    password="your_password"
)

# Option 2: CloudSession (recommended for multi-service)
from hfortix_core.session import CloudSession

with CloudSession(api_id="...", password="...") as session:
    client = FortiZTP(session=session)  # Auto-uses "fortiztp" client_id
    devices = client.api.devices.list()
    # Token managed automatically

# Option 2b: CloudSession with global rate limiting (NEW)
from hfortix_core.session import CloudSession
from hfortix_fortiztp import FortiZTP
from hfortix_forticare import FortiCare

# Configure rate limiting once at session level
session = CloudSession(
    api_id="your_api_id",
    password="your_password",
    # Global rate limiting - inherited by all clients
    rate_limit=True,
    rate_limit_max_requests=100,      # 100 requests per hour
    rate_limit_window_seconds=3600.0, # 1 hour window
    circuit_breaker=True,
)

# Both clients inherit session's rate limiting
fz = FortiZTP(session=session)     # Inherits 100 req/hour
fc = FortiCare(session=session)    # Inherits 100 req/hour

# Or override for specific client
fz_fast = FortiZTP(
    session=session,
    rate_limit_max_requests=200,   # Override session's 100 req/hour
)

# Option 3: Pre-obtained OAuth token
client = FortiZTP(oauth_token="your_token")

# List all devices
devices = client.api.devices.list()
print(f"Found {len(devices.devices)} devices")

# List provisioned devices only
provisioned = client.api.devices.list(provision_status="provisioned")

# Get specific device details
device = client.api.devices.get(device_sn="FGT60FTK19000001")
print(f"Device: {device.deviceType} - Status: {device.provisionStatus}")

# Get system status
status = client.api.system.get()
print(f"System status: {status.serviceStatus}")

Rate Limit Enforcement (NEW)

NEW in 0.5.162+: Enforce rate limits using token bucket algorithm with queue support:

from hfortix_fortiztp import FortiZTP

# Option 1: Direct client with rate limiting
client = FortiZTP(
    api_id="your_api_id",
    password="your_password",
    rate_limit=True,
    rate_limit_max_requests=2000,     # FortiZTP: 2000/hour
    rate_limit_window_seconds=3600.0, # 1 hour window
    rate_limit_strategy="queue",      # Queue excess requests
    rate_limit_queue_size=100,        # Max 100 queued requests
)

# Requests automatically queued when limit exceeded
for i in range(2100):
    devices = client.api.devices.list()  # First 2000 immediate, next 100 queued

# Option 2: CloudSession with global rate limiting (recommended)
from hfortix_core.session import CloudSession

session = CloudSession(
    api_id="your_api_id",
    password="your_password",
    rate_limit=True,
    rate_limit_max_requests=2000,
    rate_limit_window_seconds=3600.0,
)

# FortiZTP inherits session's rate limiting
client = FortiZTP(session=session)

Rate Limiting Parameters:

  • rate_limit: bool = False - Enable/disable rate limiting
  • rate_limit_strategy: str = "queue" - Strategy: queue, drop, or raise
  • rate_limit_max_requests: int = 100 - Max requests per window
  • rate_limit_window_seconds: float = 60.0 - Time window in seconds
  • rate_limit_queue_size: int = 100 - Max queued requests
  • rate_limit_queue_timeout: float = 30.0 - Max wait time in queue
  • rate_limit_queue_overflow: str = "block" - Overflow behavior: block, drop, or raise
  • circuit_breaker: bool = False - Enable circuit breaker
  • circuit_breaker_threshold: int = 5 - Failures before opening
  • circuit_breaker_timeout: float = 60.0 - Seconds before retry
  • circuit_breaker_half_open_calls: int = 3 - Test calls in half-open state

Rate Limit Tracking

FortiZTP API has a limit of 2000 calls per hour. Track your usage:

from hfortix_fortiztp import FortiZTP

# Configure custom limits (optional, defaults to None)
client = FortiZTP(
    api_id="...",
    password="...",
    rate_limit_calls_per_hour=2000  # FortiZTP: 2000/hour
)

# Make API calls
devices = client.api.devices.list()

# Check rate limit status
status = client.get_rate_limit_status()
print(f"Calls last min: {status['calls_last_min']}")
print(f"Calls last 5min: {status['calls_last_5min']}")
print(f"Calls last hour: {status['calls_last_hour']}/{status['limits']['calls_per_hour']}")
print(f"Within limits: {status['within_limits']}")

Note: Rate limiting counters are informational only and do not enforce limits.


API Coverage

Devices API (5 endpoints)

  • list() - List devices with filters (status, type, SN)
  • bulk_provision() - Provision/unprovision multiple devices
  • get() - Get single device details
  • update() - Provision/unprovision single device
  • firmware_profiles() - Get firmware profiles by region

Scripts API (7 endpoints)

  • scripts_list() - List all scripts
  • scripts_post() - Create new script
  • scripts_get() - Get script metadata
  • scripts_put() - Update script metadata
  • scripts_delete() - Delete script
  • scripts_content_get() - Download script content
  • scripts_content_put() - Upload script content

FortiManagers API (5 endpoints)

  • fortimanagers_list() - List all FortiManager configs
  • fortimanagers_post() - Create FortiManager config
  • fortimanagers_get() - Get FortiManager details
  • fortimanagers_put() - Update FortiManager config
  • fortimanagers_delete() - Delete FortiManager config

System API (1 endpoint)

  • get() - Get system status
  • Script association

System (1 endpoint)

  • Get system status

Planned Usage

Note: This is the intended API design. Code is not yet generated.

Basic Usage

from hfortix import FortiZTP

# Initialize client with OAuth credentials
client = FortiZTP(
    client_id="your_client_id",
    api_key="your_api_key",
    password="your_password"
)

# List all provisioned devices
response = client.v2.devices.list.get(provision_status="provisioned")

# Access response properties
print(f"Total devices: {response.total}")
print(f"Has cache: {response.hasCache}")

for device in response.data:
    print(f"{device['deviceSN']}: {device['provisionStatus']}")

# Get specific device
device = client.v2.devices.get.get(device_sn="FGT60D4615067214")
print(f"Device type: {device.deviceType}")
print(f"Status: {device.provisionStatus}")

# Provision a device to FortiManager
client.v2.devices.put.put(
    device_sn="FGT60D4615067214",
    request_body={
        "provisionStatus": "provisioned",
        "provisionTarget": "FortiManager",
        "provisionTargetOid": 123
    }
)

Type Safety with Literals

from typing import Literal

# IDE will autocomplete these values:
DeviceType = Literal["FortiGate", "FortiAP", "FortiSwitch", "FortiExtender"]
ProvisionStatus = Literal["provisioned", "unprovisioned", "hidden", "incomplete"]
ProvisionTarget = Literal["FortiManager", "FortiGateCloud", "FortiEdgeCloud", "ExternalController"]

# Strongly typed parameters
response = client.v2.devices.list.get(
    provision_status="provisioned",  # Autocomplete!
    device_type="FortiGate"           # Autocomplete!
)

Script Management

# List all scripts
scripts = client.v2.settings.scripts.list.get()
print(f"Total scripts: {scripts.total}")

# Create new script
new_script = client.v2.settings.scripts.post.post(
    request_body={
        "name": "Initial Configuration",
    }
)
print(f"Created script OID: {new_script.oid}")

# Upload script content
client.v2.settings.scripts.put_content.put(
    oid=new_script.oid,
    file_path="./scripts/initial_config.conf"
)

# Download script content
content = client.v2.settings.scripts.get_content.get(oid=new_script.oid)
print(content.raw)  # Plain text content

FortiManager Configuration

# Add FortiManager
fmg = client.v2.settings.fortimanagers.post.post(
    request_body={
        "sn": "FMG-VMTM23010656",
        "ip": "192.168.223.20",
        "scriptOid": 123  # Optional pre-run script
    }
)

# HA Setup (comma-separated values)
fmg_ha = client.v2.settings.fortimanagers.post.post(
    request_body={
        "sn": "FMG-VMTM23010656,FMG-VMTM23010657",
        "ip": "192.168.223.20,192.168.223.21",
        "scriptOid": 123
    }
)

# List all FortiManagers
all_fmgs = client.v2.settings.fortimanagers.list.get()
for fmg in all_fmgs.data:
    print(f"{fmg['sn']} at {fmg['ip']}")

HTTP Metadata Access

response = client.v2.devices.get.get(device_sn="FGT60D4615067214")

# HTTP metadata
print(f"Status code: {response.http_status_code}")
print(f"Response time: {response.response_time}s")

# Raw access
raw_dict = response.raw
copy_dict = response.dict()

Error Handling

from hfortix_core.exceptions import (
    AuthenticationError,
    PermissionDeniedError,
    ResourceNotFoundError,
    RateLimitError
)

try:
    device = client.v2.devices.get.get(device_sn="INVALID_SN")
except ResourceNotFoundError as e:
    print(f"Device not found: {e}")
except PermissionDeniedError as e:
    print(f"Access denied: {e}")
except RateLimitError as e:
    print(f"Rate limit exceeded: {e}")
    print(f"Retry after: {e.retry_after}s")

Development Status

Completed ✅

  • Generator architecture designed
  • Schema file structure created
  • Type definitions (5 Literal types)
  • Common data structures (5 structures)
  • All 18 endpoints documented
  • 6 endpoints with complete responses

In Progress ⏳

  • Complete schema (12 endpoints missing responses)
  • Verify data structures
  • Get firmware_profiles structure

Not Started 📋

  • Schema parser
  • Endpoint generator adaptation
  • Template creation
  • Code generation
  • Testing
  • Documentation
  • PyPI release

Project Structure

packages/fortiztp/
├── README.md                          # This file
├── dev/
│   ├── GENERATOR_PLAN.md              # Implementation roadmap
│   ├── API_DOCUMENTATION.md           # API reference
│   ├── SCHEMA_GAPS_ANALYSIS.md        # Missing information checklist
│   ├── generator/
│   │   ├── generate.py                # Main generator script (to be created)
│   │   ├── schema/
│   │   │   └── fortiztp_schema.py     # ✅ Manual API schema
│   │   ├── parsers/
│   │   │   └── fortiztp_schema_parser.py  # Schema parser (to be created)
│   │   ├── generators/
│   │   │   └── endpoint_generator.py  # Code generator (to be adapted)
│   │   ├── templates/
│   │   │   ├── endpoint_template.py.tmpl   # (to be created)
│   │   │   └── endpoint_template.pyi.tmpl  # (to be created)
│   │   └── endpoint_config.py         # FortiZTP overrides (to be created)
│   └── tests/                         # Generator tests (to be created)
└── src/
    └── hfortix_fortiztp/              # Generated SDK code (not yet created)
        ├── __init__.py
        ├── models.py                  # Response classes
        ├── types.py                   # Literal type aliases
        ├── api/
        │   └── v2/
        │       ├── __init__.py
        │       ├── devices/
        │       ├── settings/
        │       │   ├── scripts/
        │       │   └── fortimanagers/
        │       └── system/
        └── py.typed

Schema Status

Type Definitions (5/5) ✅

  • DeviceType
  • ProvisionStatus
  • ProvisionSubStatus
  • ProvisionTarget
  • ServiceStatus

Data Structures (5/5) ✅

  • DEVICE_V2_DATA (16 properties) - needs verification
  • SCRIPT_META_DATA (3 properties) - needs verification
  • FORTIMANAGER_META_DATA (5 properties)
  • SYSTEM_DATA (4 properties)
  • ERROR_RESPONSE (2 properties)

Endpoints (6/18) ⚠️

Complete:

  • ✅ GET /v2/setting/fortimanagers
  • ✅ POST /v2/setting/fortimanagers
  • ✅ GET /v2/setting/fortimanagers/{oid}
  • ✅ PUT /v2/setting/fortimanagers/{oid}
  • ✅ DELETE /v2/setting/fortimanagers/{oid}
  • ✅ GET /v2/system

Missing Response Definitions (12):

  • ❌ GET /v2/devices
  • ❌ PUT /v2/devices
  • ❌ GET /v2/devices/{deviceSN}
  • ❌ PUT /v2/devices/{deviceSN}
  • ❌ GET /v2/devices/{deviceSN}/regions/{region}/firmwareprofiles
  • ❌ GET /v2/setting/scripts
  • ❌ POST /v2/setting/scripts
  • ❌ GET /v2/setting/scripts/{oid}
  • ❌ PUT /v2/setting/scripts/{oid}
  • ❌ DELETE /v2/setting/scripts/{oid}
  • ❌ GET /v2/setting/scripts/{oid}/content
  • ❌ PUT /v2/setting/scripts/{oid}/content

Next Steps

Critical (Blocking Code Generation):

  1. Get response body examples for 12 missing endpoints
  2. Verify DEVICE_V2_DATA structure (all 16 fields correct?)
  3. Get firmware_profiles actual response structure
  4. Confirm HTTP status codes (200, 201, 204)

Implementation:

  1. Create schema parser
  2. Adapt endpoint generator from FortiCare
  3. Create templates
  4. Generate code
  5. Test and validate

Resources


Contributing

This package is part of the hfortix SDK family:

  • hfortix-core: Core HTTP client and utilities
  • hfortix-fortios: FortiOS REST API
  • hfortix-forticare: FortiCare Asset Management API
  • hfortix-fortiztp: FortiZTP Cloud API (this package)
  • hfortix: Meta package

See main repository for contribution guidelines.


License

[Your License Here]


Authors

[Your Name/Organization]


Last Updated: February 6, 2026
Schema Version: 2.0 (33% complete)
Status: Development (blocked - awaiting API documentation)

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

hfortix_fortiztp-0.5.162.tar.gz (26.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

hfortix_fortiztp-0.5.162-py3-none-any.whl (31.0 kB view details)

Uploaded Python 3

File details

Details for the file hfortix_fortiztp-0.5.162.tar.gz.

File metadata

  • Download URL: hfortix_fortiztp-0.5.162.tar.gz
  • Upload date:
  • Size: 26.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for hfortix_fortiztp-0.5.162.tar.gz
Algorithm Hash digest
SHA256 1638cd32a574e4107ff689985fb27829d6a41caa9b795f39d0001955b08a2cd6
MD5 66a86cfd8d63d8f286e760d657b70a4d
BLAKE2b-256 6128698ffc715419132d800271a2970ff51d63500cadd8bd73f1118ccd8e46a8

See more details on using hashes here.

File details

Details for the file hfortix_fortiztp-0.5.162-py3-none-any.whl.

File metadata

File hashes

Hashes for hfortix_fortiztp-0.5.162-py3-none-any.whl
Algorithm Hash digest
SHA256 cc705fed62eb72569e047e157d357cc479f7a9d5454fe27ef829ce0e9c6d24c9
MD5 c7c527b296939d404269154cd21af64f
BLAKE2b-256 2432fb5d7382c79427246ce7a23956396f7225f50579786e8d9c475582c9c48d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page